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)
//优化建议
@Slf4j
public 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代替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 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