为什么ArrayList线程不安全?为什么ArrayList在多线程情况下会报ConcurrentModificationException?
看下面的第一个例子,并发读写:
package com.andy.juc; import java.util.ArrayList; import java.util.List; import java.util.UUID; public class UnsafeList { public static void main(String[] args) { List<String> list=new ArrayList<>(); System.out.println(list); for(int i=0;i<10;i++){ new Thread(new Runnable() { @Override public void run() { list.add(UUID.randomUUID().toString()); System.out.println(list); } },String.valueOf(i)).start(); } } }
上面的代码在System.out.println(list);这一行为报错,报错信息如下。
Exception in thread "0" Exception in thread "8" java.util.ConcurrentModificationException at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901) at java.util.ArrayList$Itr.next(ArrayList.java:851) at java.util.AbstractCollection.toString(AbstractCollection.java:461) at java.lang.String.valueOf(String.java:2994) at java.io.PrintStream.println(PrintStream.java:821) at com.andy.juc.UnsafeList$1.run(UnsafeList.java:17) at java.lang.Thread.run(Thread.java:745)
原理如下,迭代器读next元素的时候,会检查modcount和最开始的modcount是不是一样的,相当于一个乐观锁。
而在ArrayList的add方法里面,每次新增一个元素会把modcount给加1.
所以modcount修改之后,就会报这个错误。
需要注意的是,如果只是对ArrayList进行add操作,并不会发生ConcurrentModification报错,这个也是面试经常问的问题。
import java.util.ArrayList; import java.util.List; import java.util.UUID; public class UnsafeList { public static void main(String[] args) { List<String> list=new ArrayList<>(); System.out.println(list); for(int i=0;i<100;i++){ new Thread(new Runnable() { @Override public void run() { list.add(UUID.randomUUID().toString()); } },String.valueOf(i)).start(); } try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(list); } }
扩展只是
为什么ArrayList底层用了数组,但是可以不用指定数组的大小?
因为ArrayList需要扩容,扩容的时候用了新的数组。
下面看一个ArrayList并发写的多线程问题,创建的size不是10000个,而是小于10000个:(多线程的例子一次有可能测不出效果,可以多试几次,或者增加线程个数)
import java.util.ArrayList; import java.util.List; import java.util.UUID; public class UnsafeList { public static void main(String[] args) { List<String> list=new ArrayList<>(); System.out.println(list); for(int i=0;i<10000;i++){ new Thread(new Runnable() { @Override public void run() { list.add(UUID.randomUUID().toString().substring(0,6)); } },String.valueOf(i)).start(); } try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(list.size()); System.out.println(list); } }
执行结果如下: