ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

CopyOnWriteArrayList线程安全分析

2020-05-26 12:51:23  阅读:279  来源: 互联网

标签:index elements 容器 newElements CopyOnWriteArrayList 安全 线程 len


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操作,一定要谨慎处理。

 

标签:index,elements,容器,newElements,CopyOnWriteArrayList,安全,线程,len
来源: https://www.cnblogs.com/baichunyu/p/12964819.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有