CopyOnWriteArrayList是开发过程中常用的一种并发容器,多用于读多写少的并发场景。但是CopyOnWriteArrayList真的能做到完全的线程安全吗? 答案是并不能。

一、CopyOnWriteArrayList原理

  我们可以看出当我们向容器添加或删除元素的时候,不直接往当前容器添加删除,而是先将当前容器进行Copy,复制出一个新的容器,然后新的容器里添加删除元素,添加删除完元素之后,再将原容器的引用指向新的容器,整个过程加锁,保证了写的线程安全。

 1     public boolean add(E e) {
2 synchronized (lock) {
3 Object[] elements = getArray();
4 int len = elements.length;
5 Object[] newElements = Arrays.copyOf(elements, len + 1);
6 newElements[len] = e;
7 setArray(newElements);
8 return true;
9 }
10 }
11
12 public E remove(int index) {
13 synchronized (lock) {
14 Object[] elements = getArray();
15 int len = elements.length;
16 E oldValue = get(elements, index);
17 int numMoved = len - index - 1;
18 if (numMoved == 0)
19 setArray(Arrays.copyOf(elements, len - 1));
20 else {
21 Object[] newElements = new Object[len - 1];
22 System.arraycopy(elements, 0, newElements, 0, index);
23 System.arraycopy(elements, index + 1, newElements, index,
24 numMoved);
25 setArray(newElements);
26 }
27 return oldValue;
28 }
29 }

  而因为写操作的时候不会对当前容器做任何处理,所以我们可以对容器进行并发的读,而不需要加锁,也就是读写分离。

1     public E get(int index) {
2 return get(getArray(), index);
3 }

  一般来讲我们使用时,会用一个线程向容器中添加元素,一个线程来读取元素,而读取的操作往往更加频繁。写操作加锁保证了线程安全,读写分离保证了读操作的效率,简直完美。

二、数组越界

  但想象一下如果这时候有第三个线程进行删除元素操作,读线程去读取容器中最后一个元素,读之前的时候容器大小为i,当去读的时候删除线程突然删除了一个元素,这个时候容器大小变为了i-1,读线程仍然去读取第i个元素,这时候就会发生数组越界。

  测试一下,首先向CopyOnWriteArrayList里面塞10000个测试数据,启动两个线程,一个不断的删除元素,一个不断的读取容器中最后一个数据。

 1     public void test(){
2 for(int i = 0; i<10000; i++){
3 list.add("string" + i);
4 }
5
6 new Thread(new Runnable() {
7 @Override
8 public void run() {
9 while (true) {
10 if (list.size() > 0) {
11 String content = list.get(list.size() - 1);
12 }else {
13 break;
14 }
15 }
16 }
17 }).start();
18
19 new Thread(new Runnable() {
20 @Override
21 public void run() {
22 while (true) {
23 if(list.size() <= 0){
24 break;
25 }
26 list.remove(0);
27 try {
28 Thread.sleep(10);
29 } catch (InterruptedException e) {
30 e.printStackTrace();
31 }
32 }
33 }
34 }).start();
35 }

  运行,可以看出删除到第7个元素的时候就发生了数组越界:

 

  从上可以看出CopyOnWriteArrayList并不是完全意义上的线程安全,如果涉及到remove操作,一定要谨慎处理。

CopyOnWriteArrayList线程安全分析的更多相关文章

  1. CopyOnWriteArrayList 源码分析 基于jdk1.8

    CopyOnWriteArrayList  源码分析: 1:成员属性: final transient ReentrantLock lock = new ReentrantLock();  //内部是 ...

  2. Java线程问题分析定位

    Java线程问题分析定位 分析步骤: 1.使用top命令查看系统资源占用情况,发现Java进程占用大量CPU资源,PID为11572: 2.显示进程详细列表命令:ps -mp 11572 -o THR ...

  3. Android系统--输入系统(七)Reader_Dispatcher线程启动分析

    Android系统--输入系统(七)Reader_Dispatcher线程启动分析 1. Reader/Dispatcher的引入 对于输入系统来说,将会创建两个线程: Reader线程(读取事件) ...

  4. Android系统--输入系统(十三)Dispatcher线程情景分析_Reader线程传递事件

    Android系统--输入系统(十三)Dispatcher线程情景分析_Reader线程传递事件 1. 输入按键 我们知道Android系统的按键分为三类:(1)Global Key;(2)Syste ...

  5. Android系统--输入系统(十四)Dispatcher线程情景分析_dispatch前处理

    Android系统--输入系统(十四)Dispatcher线程情景分析_dispatch前处理 1. 回顾 我们知道Android输入系统是Reader线程通过驱动程序得到上报的输入事件,还要经过处理 ...

  6. Spring中获取request的几种方法,及其线程安全性分析

    前言 本文将介绍在Spring MVC开发的web系统中,获取request对象的几种方法,并讨论其线程安全性. 原创不易,如果觉得文章对你有帮助,欢迎点赞.评论.文章有疏漏之处,欢迎批评指正. 欢迎 ...

  7. 【转】java线上程序排错经验2 - 线程堆栈分析

    前言 在线上的程序中,我们可能经常会碰到程序卡死或者执行很慢的情况,这时候我们希望知道是代码哪里的问题,我们或许迫切希望得到代码运行到哪里了,是哪一步很慢,是否是进入了死循环,或者是否哪一段代码有问题 ...

  8. [No000016E]Spring 中获取 request 的几种方法,及其线程安全性分析

    前言 本文将介绍在Spring MVC开发的web系统中,获取request对象的几种方法,并讨论其线程安全性. 原创不易,如果觉得文章对你有帮助,欢迎点赞.评论.文章有疏漏之处,欢迎批评指正. 欢迎 ...

  9. java线程dump分析工具

    jstack和线程dump分析  java程序性能分析之thread dump和heap dump 一.[内存dump] jmap –dump:live,format=b,file=heap.bin ...

随机推荐

  1. 什么是64位和32位internet explorer

    什么是64位和32位internet explorer 如果您使用 64 位版本的 Internet Explorer 时,您会遇到问题,请尝试使用 32 位版本的 Internet Explorer ...

  2. 简化ETL工作,编写一个Canal胶水层

    前提 这是一篇憋了很久的文章,一直想写,却又一直忘记了写.整篇文章可能会有点流水账,相对详细地介绍怎么写一个小型的"框架".这个精悍的胶水层已经在生产环境服役超过半年,这里尝试把耦 ...

  3. Arduino 串口的一些高级用法

    来源: 1.配置串口通信数据位.校验位.停止位 通常我们使用Serial.begin(speed)来完成串口的初始化,这种方式,只能配置串口的波特率. 而使用Serial.begin(speed, c ...

  4. Go 接口类型

    接口作用 Go语言中的接口是一种类型,类似于Python中的抽象基类. Go语言中使用接口来体现多态,是duck-type的一种体现. 如,只要一个东西会叫,会走,那么我们就可以将它定义为一个动物的接 ...

  5. CRUD,分页,排序,搜索与AngularJS在MVC

    下载source - 53.1 MB 介绍 在选择最新的技术时,有几个因素会起作用,包括这些技术将如何与我们的项目集成.这篇文章解决了开始使用AngularJS和MVC的乞丐的问题.这篇文章告诉使用语 ...

  6. MySQL 复制表(表结构、表结构和数据)

    MySQL 中使用 命令行 复制表结构及数据的方法主要有以下几种: 1.只复制表结构 CREATE TABLE new_table SELECT * FROM old_table WHERE 1=2: ...

  7. CV学习日志:CV开发常用库及其头文件

    CV开发过程中,通常会涉及以下库:(1)语言/视觉:C.CPP.QT.OpenCV(2)通信/模拟:ROS2.Gazebo.Webots(3)日志/数学:Eigen3.Gflags.Glog.Cere ...

  8. 通过MapReduce降低服务响应时间

    在微服务中开发中,api网关扮演对外提供restful api的角色,而api的数据往往会依赖其他服务,复杂的api更是会依赖多个甚至数十个服务.虽然单个被依赖服务的耗时一般都比较低,但如果多个服务串 ...

  9. java性能分析之火焰图

    原由 最近因为kafka.zookeeper.ES和相关的Java应用的内存问题搞的头大,做运维将近4年,对Java调优.性能方面的知识了解的少之又少,是时候下定决心来对他多一个学习了.不能一口吃成一 ...

  10. 集训DP复习整理

    DP复习 集训%你赛2:测绘(审题DP) 经过2000+个小时的努力终于把这道题做出来的蒟蒻通 分析: 这道题我一直没做出来的原因就是因为我太蒟了题面看不懂,题面读懂了,其实不是特别难. 题目翻译: ...