在多线程的情况下使用ArrayList进行读写的时候会出现java.util.ConcurrentModificationException,称为并发修改异常。
代码如下
public class NotSafeDemo { public static void main(String[] args) { //new Vector //ArrayList<String> list = new ArrayList<String>(); //List<String> list=Collections.synchronizedList(new ArrayList()); //使用CopyOnWriteArrayList(); CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>(); for (int i = 0; i <30; i++) { new Thread(()->{ list.add(UUID.randomUUID().toString().substring(0,8)); System.out.println(list); },String.valueOf(i)).start(); } }
导致ArrayList不安全的原因
add是没有加锁的
public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; }
解决方案:
- 使用Vector(不建议)
- 使用Collections.synchronizedList(new ArrayList());
- 使用java.util.concurrent包下的CopyOnWriteArrayList;
小总结:
/* * 笔记 * 写复制 * CopyOnWrite容器即写复制容器。往一个容器添加元素的时候,不直接往当前容器Object[]添加,而是将当前容器Object[]进行copy, * 复制出一个新的容器Object[] newElements,然后新的容器Object[] newElements里添加元素,添加完元素之后 * 再将原容器的引用指向新的容器 setArray(newElements);这样做的好处是可以对CopyOnWrite容器进行并发读写, * 而不需要加锁,因为当前容器不会添加任何元素。所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器 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(); } } */
同样的问题hashSet也是存在的
代码
HashSet<String> set = new HashSet<String>(); for (int i = 0; i < 30; i++) { new Thread(()->{ set.add(UUID.randomUUID().toString().substring(0,8)); System.out.println(set); },String.valueOf(i)).start(); } 出现 java.util.ConcurrentModificationException
修改成
//HashSet<String> set = new HashSet<String>(); Set<String> set = new CopyOnWriteArraySet<String>(); for (int i = 0; i < 30; i++) { new Thread(()->{ set.add(UUID.randomUUID().toString().substring(0,8)); System.out.println(set); },String.valueOf(i)).start(); }
hashSet底层是hashMap。hashmap的初始值为16.负载因子为0.75。高并发的的时候很少使用hashMap,多使用
HashMap也是不安全的
代码:
//Map<String, String> map = Collections.synchronizedMap(new HashMap<>());//new HashMap<>(); Map<String,String> map=new ConcurrentHashMap(); for (int i = 0; i < 30; i++) { new Thread(()-> { map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0, 8)); System.out.println(map); },String.valueOf(i)).start(); }