在并发条件下,由于多数集合没有同步控制所以这些集合具有线程不安全性
线程不安全的集合
线程不安全用例(ArrayList为例)
示例
public class MainTest {
public static void main(String[] args) {
ArrayList<String> arrayList = new ArrayList<>();
for(int i=0; i< 10; i++) {
new Thread(() -> {
arrayList.add(UUID.randomUUID().toString());
System.out.println(arrayList);
},String.valueOf(i)).start();
}
}
}
运行会抛出
java.util.ConcurrentModificationException
并发修改异常
出现该异常的原因是,当某个线程正在执行 add()方法时,被某个线程打断,添加到一半被打断,没有被添加完。
解决ArrayList线程不安全问题
- 可以使用 Vector 来代替 ArrayList,Vector 是线程安全的 ArrayList,但是由于,并发量太小,被淘汰;
- 使用 Collections.synchronizedArrayList() 来创建 ArrayList;使用 Collections 工具类来创建 ArrayList 的思路是,在 ArrayList 的外边套了一个synchronized外壳,来使 ArrayList 线程安全;
- 使用 CopyOnWriteArrayList()来保证 ArrayList 线程安全;
CopyOnWriteArrayList
此类采用了写时复制技术,从读写分离的角度去保证了线程的安全性
CopyWriteArrayList之所以线程安全的原因是在源码里面使用 ReentrantLock,所以保证了某个线程在写的时候不会被打断;
/** The array, accessed only via getArray/setArray. */
private transient volatile Object[] array;
/** The lock protecting all mutators */
final transient ReentrantLock lock = new ReentrantLock();
/**
* Gets the array. Non-private so as to also be accessible
* from CopyOnWriteArraySet class.
*/
final Object[] getArray() {
return array;
}
/**
* 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();
}
}
可以看到源码开始先是复制了一份数组(因为同一时刻只有一个线程写,其余的线程会读),在复制的数组上边进行写操作,写好以后在返回 true。
这样写的就把读写进行了分离.写好以后因为 array 加了 volatile 关键字,所以该数组是对于其他的线程是可见的,就会读取到最新的值
集合对应线程安全类
List | CopyOnWriteArrayList |
---|---|
Set | CopyOnWriteArraySet |
Map | ConcurrentHashMap |
ConcurrentHashMap实现线程安全原理与其他不一致
ConcurrentHashMap