我们都是知道像常用的ArrayList、LinkedList、hashSet、hashMap等都是线程不安全的集合,在多线程下可能会出现
并发修改异常:
ConcurrentModificationException
,那没如何来使用线程安全的集合呢?
vector
和
HashTable
就不说了,因为他虽然是线程安全的集合,但是实现方式是在底层方法上全都加了
Synchronized
来修饰的,效率低。
线程安全的List
Collections工具类提供了创建线程安全的List的方法:
List<String> list = Collections.synchronizedList(new ArrayList<> ());
不过这里我们重点说JUC
下的CopyOnWriteArrayList
CopyOnWrite:写入时复制思想,简称COW,是计算机程序设计领域的一种优化策略
创建方式:
List<String> list = new CopyOnWriteArrayList<>();
实现原理:
我们先阅读其源码了解下它是如何实现的。以下代码是向CopyOnWriteArrayList中add方法的实现(向CopyOnWriteArrayList里添加元素),可以发现在添加的时候是加了ReentrantLock
锁,并且是先Copy出副本添加,最后再赋值。
/**
* Appends the specified element to the end of this list.
* @param e element to be appended to this list
* @return <tt>true</tt> (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();
}
}
读的时候不需要加锁,如果读的时候有多个线程正在向CopyOnWriteArrayList添加数据,读还是会读到旧的数据,因为写的时候不会锁住旧的List。
public E get(int index) {
return get(getArray(), index);
}
线程安全的Set
Collections工具类提供了创建线程安全的Set的方法:
Set<String> set = Collections.synchronizedSet(new HashSet<>());
同上,JUC下有Set<String> set = new CopyOnWriteArraySet<>();
这里额外说下,hashSet 底层是什么?
其实hashSet底层就是用的hashMap的Key,而value就是一个用static和final修饰的object不变值。
线程安全的Map
Collections工具类提供了创建线程安全的Map的方法:
Collections.synchronizedMap(new HashMap<>());
这里可不能同上了,因为JDK并没有给我们提供“CopyOnWriteMap”,而是提供了一个ConcurrentHashMap
。
Map<String, String> map = new ConcurrentHashMap<>();
jdk1.7中ConcurrentHashMap相对于HashMap其实多了一个Segment
的的角色,它其实是一种可重入锁(ReentrantLock),在ConcurrentHashMap里扮演锁的角色。
jdk1.8中放弃了Segment臃肿的分段锁设计,取而代之的是采用Node + CAS + Synchronized来保证并发安全进行实现。