CopyOnWriteArrayList线程安全分析

java问题王 Java每日一问 发布时间:2021-10-19 11:12:50 阅读数:20218 1

CopyOnWriteArraylist简介说明

CopyOnWriteArrayList是java中常用的一种并发容器
多用于读多写少的并发场景
但是注意:CopyOnWriteArrayList并不是线程安全

CopyOnWriteArrayList原理

CopyOnWriteArrayList向容器中添加或删除元素时,不直接往当前容器添加删除,而是先将当前容器进行Copy,复制出一个新的容器
然后新的容器里添加删除元素,添加删除完元素之后
再将原容器的引用指向新的容器,整个过程加锁,保证了写的线程安全
例:
 
      public boolean add(E e) {
         synchronized (lock) {
            Object[] elements = getArray();
              int len = elements.length;
             Object[] newElements = Arrays.copyOf(elements, len + 1);
              newElements[len] = e;
             setArray(newElements);
             return true;
         }
    }
 
     public E remove(int index) {
        synchronized (lock) {
             Object[] elements = getArray();
             int len = elements.length;
             E oldValue = get(elements, index);
             int numMoved = len - index - 1;
             if (numMoved == 0)
                 setArray(Arrays.copyOf(elements, len - 1));
             else {
                 Object[] newElements = new Object[len - 1];
                 System.arraycopy(elements, 0, newElements, 0, index);
                 System.arraycopy(elements, index + 1, newElements, index,
                                  numMoved);
                 setArray(newElements);
             }
             return oldValue;
         }
     }
上述的思路,写操作不会对当前容器进行任何操作
那么对容器的并发读,则无需加锁,这是一种读写分离的思想
public E get(int index) {
         return get(getArray(), index);
     }
通常我们会使用一个线程向容器中添加元素
一个线程来读取元素,而读取的操作往往更加频繁
写操作加锁保证了线程安全,读写分离保证了读操作的效率

数组越界注意事项

当有一个线程进行删除元素操作
读线程去读取容器中最后一个元素
读之前的时候容器大小为i
当去读的时候删除线程突然删除了一个元素
这个时候容器大小变为了i-1
读线程仍然去读取第i个元素,这时候就会发生数组越界
例:
CopyOnWriteArrayList删除测试 向list中放入10000个测试数据
启动两个线程
一个不断的删除元素
一个不断的读取容器中最后一个数据
      public void test(){
          for(int i = 0; i<10000; i++){
              list.add("string" + i);
         }
  
          new Thread(new Runnable() {
              @Override
              public void run() {
                  while (true) {
                     if (list.size() > 0) {
                         String content = list.get(list.size() - 1);
                     }else {
                         break;
                     }
                 }
             }
         }).start();
 
         new Thread(new Runnable() {
             @Override
             public void run() {
                 while (true) {
                     if(list.size() <= 0){
                         break;
                     }
                     list.remove(0);
                     try {
                         Thread.sleep(10);
                     } catch (InterruptedException e) {
                         e.printStackTrace();
                     }
                 }
             }
         }).start();
     }
从以上的代码中,我们可以看出并不是线程安全,
当涉及remove操作时,可能会产生异常
版权声明

本文仅代表作者观点,不代表本站立场。
本文系作者授权发表,未经许可,不得转载。

本文链接: https://www.Java265.com/JavaProblem/202110/1500.html

最近发表

热门文章

好文推荐

Java265.com

https://www.java265.com

站长统计|粤ICP备14097017号-3

Powered By Java265.com信息维护小组

使用手机扫描二维码

关注我们看更多资讯

java爱好者