ArrayList集合在多线程并发操作下是不安全的
ArrayList集合在并发操作下会发生异常:故障现象java.util.ConcurrentModificationException
导致原因 多线程对集合的并发操作导致并发修改异常的产生
解决方案
1、使用Vector(加synchronized)
2、使用集合工具类里的Collections.synchronizedList(new ArrayList<>())
使用同步代码块
3、CopyOnWriteArrayList
(写时复制ReentrantLock
)
CopyOnWriteArrayList
使用的思想是读写分离
public class Demo3 {public static void main(String[] args) {//List<String> list = new ArrayList<>();//List<String> list = Collections.synchronizedList(new ArrayList<>());//collections是集合接口的工具类,collection是集合的接口/*** 写时复制* CopyOnWrite容器即写时复制的容器,往一个容器添加元素的时候,不直接往当前容器object[]* 而是先将当前容器object[]进行复制,复制出一个新的object[] new Elements,然后新的容器object[] new Elements里添加元素* 添加完元素之后,再将原容器的引用指向新的容器,这样做的好处是可以对容器进行并发的读,* 而不需要加锁,因为当前容器不会添加任何元素,所以copyonwrite是一种读写分离的思想,读和写不同的容器*/List<String> list = new CopyOnWriteArrayList<>();for (int i = 1; i <= 30; i++) {new Thread(()->{list.add(UUID.randomUUID().toString().substring(0, 10));System.out.println(list);}).start();}}
package com.zhbit.juc;import lombok.extern.slf4j.Slf4j;import java.util.*;import java.util.concurrent.CopyOnWriteArrayList;import java.util.concurrent.TimeUnit;//ArrayList集合类不安全//故障现象 java.util.ConcurrentModificationException//导致原因 多线程对集合的并发操作导致并发修改异常的产生//解决方案 Vector(加synchronized)--->Collections.synchronizedList(new ArrayList<>())使用同步代码块--->CopyOnWriteArrayList(写时复制ReentrantLock)//优化建议@Slf4jpublic class CopyOnWriteArrayListExample {public static void main(String[] args) throws InterruptedException {CopyOnWriteArrayListExample.testArrayList();// CopyOnWriteArrayListExample.testVector();// CopyOnWriteArrayListExample.testCollectionsSynchronizedList();// CopyOnWriteArrayListExample.testCopyOnWriteArrayList();TimeUnit.SECONDS.sleep(3);}static void testArrayList() {List list = new ArrayList(); //不安全会产生 java.util.ConcurrentModificationException异常for (int i = 1; i <= 30; i ) {new Thread(() -> {list.add(UUID.randomUUID().toString().substring(0, 8));System.out.println(list);}, String.valueOf(i)).start();}}//以下是三种解决方法static void testVector() {List list = new Vector();//1、使用Vector代替ArrayListfor (int i = 1; i <= 30; i ) {new Thread(() -> {list.add(UUID.randomUUID().toString().substring(0, 8));System.out.println(list);}, String.valueOf(i)).start();}}static void testCollectionsSynchronizedList() {//2、使用集合工具类的 Collections.synchronizedList解决List list = Collections.synchronizedList(new ArrayList<>());for (int i = 1; i <= 30; i ) {new Thread(() -> {list.add(UUID.randomUUID().toString().substring(0, 8));System.out.println(list);}, String.valueOf(i)).start();}}static void testCopyOnWriteArrayList() {//3、使用CopyOnWriteArrayList(写时复制ReentrantLock)List list = new CopyOnWriteArrayList();for (int i = 1; i <= 30; i ) {new Thread(() -> {list.add(UUID.randomUUID().toString().substring(0, 8));System.out.println(list);}, String.valueOf(i)).start();}}}
源码
1、Vector添加操作
使用了synchronized
,底层关键字,重量级锁,会阻塞其它线程
/*** Appends the specified element to the end of this Vector.** @param e element to be appended to this Vector* @return {@code true} (as specified by {@link Collection#add})* @since 1.2*/public synchronized boolean add(E e) {modCount ;ensureCapacityHelper(elementCount 1);elementData[elementCount ] = e;return true;}
2、Collections.synchronizedList
添加操作
使用synchronized
同步代码块
public boolean add(E e) {synchronized (mutex) {return c.add(e);}}
3、CopyOnWriteArrayList
的添加操作
使用了ReentrantLock
锁,这是JavaAPI
层面的锁,属于轻量级的锁,会阻塞其它线程
/*** Appends the specified element to the end of this list.** @param e element to be appended to this list* @return {@code true} (as specified by {@link Collection#add})*/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(); //释放锁}}
ArrayList
剖析
底层数据结构:Object
类型的数组结构
数组的的默认值:既然底层数据结构是Object
类型的数组结构,那么数组是一段连续的内存空间
HashSet
剖析
底层数据结构: 底层数据结构是HashMap
添加操作
HashSet
的添加操作使用的是HashMap
的put
操作,来保证数据的唯一性,因为HashMap
的键是不可重复的
Collections
工具类
CopyOnWriteArrayList
写时复制
ConcurrentHashMap`底层并不是用写时复制的思想来控制多线程并发安全的,而是直接用`Node`数组 链表 红黑树的数据结构来实现,并发控制使用`Synchronized`和`CAS`来操作,整个看起来就像是优化过且线程安全的`HashMap