ArrayList线程不安全,JUC是如何处理的

1,230 阅读2分钟

当我们并发往ArrayList中添加值的时候,当并发过大的时候会出现ConcurrentModificationException(并发修改异常),

List list =new ArrayList<>();       
 for (int i = 0; i <100; i++) {       
     new Thread(()->{          
      list.add("我在这里");       
         System.out.println(list);    
       }).start();     
   }
//*****************会报出如下异常****************
Exception in thread "Thread-86" java.util.ConcurrentModificationException

老生常谈的都知道ArrayList不安全,弄段源码,这一看就不安全了

vector安全了,但有synchronized,效率低,而且操作上扩容、修改、分配内存也不大友好(小声说:synchronized其实优化这么多次了,性能已经很可以了),

,那咋办,从标题可以看出,解决方案就是JUC的写时复制(CopyOnWrite),和redis持久化差不多。在测试CopyOnWrite的时候是安全的,在1.8的时候CopyOnWrite采用的机制是ReentrantLock

 public boolean add(E e) {     
   final ReentrantLock lock = this.lock;    
    lock.lock();   
     try {        
        Object[] elements = getArray();      
        int len = elements.length;    
        Object[] newElements = Arrays.copyOf(elements, len + 1);     
        newElements[len] = e;        
        setArray(newElements);      
        return true;     
   } finally {      
      lock.unlock();     
   }  
  }

而我在jdk12上看到它又采用了synchronized

咋办呢,看看为啥呗;两个都是可重入独占锁,在不涉及到中断、超时等情况时,编码时使用synchronized明显比ReentrantLock方便简洁得多;(仔细瞅瞅会发现12比之前的版本少new了一个Object数组,哈哈);

看上面的代码后也许要问了,他这也使用了同步代码块,我自己使用同步代码块用ArrayList不香吗;,这里我们就要看下this.lock和定义的数组了;

  #transient(短暂的) Object对象充当写时复制的锁对象,不被序列化的锁对象  
  #节省空间  
 #volatile (易变的)是轻量级的锁,它只具备可见性,但没有原子特性 
 #如果一个变量加上volatile,那么只要对这个域产生了写操作, 
 #所有的读操作都可以看到这个修改,volatile会被立即写入到主内存中, 
 #而读的操作就发生在主内存中,如果多个任务在同时访问某个域, 
 #那么这个域就应该是volatilevolatile可以保证变量的安全  
 final transient Object lock = new Object();  
 private transient volatile Object[] array;

所以synchronized保证线程安全,里面的数据安全则交由volatile

看这张图的读方法,啥涉及锁的都没加所以可以并发读;

看了上面的两个定义大概应该懂了,CopyOnWrite适合于读多写少的业务,可以并发读,每次copy的时候对内存也是有很大消耗的,得具体业务具体分析;over

好啦,今天的不开心就止于此吧,明天依旧光芒万丈发啊,宝贝!

撒花,完结!!!

一个没有梦想的梦想家