List

ArrayList是非线程安全类,在多线程同时写的情况下,会抛出异常java.util.ConcurrentModificationException

1
2
3
4
5
6
7
8
9
private static void listNotSafe() {
List<String> list=new ArrayList<>();
for (int i = 1; i <= 30; i++) {
new Thread(() -> {
list.add(UUID.randomUUID().toString().substring(0, 8));
System.out.println(Thread.currentThread().getName() + "\t" + list);
}, String.valueOf(i)).start();
}
}

解决方法:

  1. 使用Vector(ArrayList所有方法加synchronized,太重)。

  2. 使用Collections.synchronizedList()转换成线程安全类。方法对变量mutex加锁实现方法线程安全(线程安全仅仅指的是直接使用它提供的函数,如list.add(),list.get(i); 也就是说仅仅是原子操作的时候才是线程安全的)

  1. 使用java.concurrent.CopyOnWriteArrayList(推荐)。

CopyOnWriteArrayList和CopyOnWriteSet

CopyOnWriteArrayList通过写时复制来实现读写分离,比如add方法,先复制一个新数组,长度+1,并且将新元素放置到最后。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public boolean add(E e) {
// 加锁
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
// 复制新数组并且长度+1
Object[] newElements = Arrays.copyOf(elements, len + 1);
newElements[len] = e;
// 设置新数组地址,旧数组等待垃圾回收
setArray(newElements);
return true;
} finally {
// 解锁
lock.unlock();
}
}

缺点:
1、 集合复制耗内存。
2、 实时性不高。

使用场景:
1、读多写少
2、集合数据量不大

CopyOnWriteSet是线程安全类,与之相反HashSetTreeSet都是线程不安全的。CopyOnWriteSet底层维护了一个CopyOnWriteArrayList