1、CopyOnWrite容器:
写时复制,也就是说当我们在容器里面增加元素的时候,会拷贝一份当前容器的元素生成一个新的容器,然后往这个新容器中添加新元素,之后再把这个新容器赋值给旧容器,也就是说原来的容器指向了这个多出一个元素的容器。从CopyOnWriteArrayList的add方法中可以看出:
private transient volatile Object[] elements;
public synchronized boolean add(E e) {
Object[] newElements = new Object[elements.length + 1];
System.arraycopy(elements, 0, newElements, 0, elements.length);
newElements[elements.length] = e;
elements = newElements;
return true;
}
可见,该方法是个同步方法,多线程环境下,同一时间只能有一个线程来操作这个数组。但是读是不加锁的,可以进行并发的读,所以我们读数据的时候,有可能只读到了旧数据的值。这也体现了读写分离的思想,读和写是不同的容器。
public E get(int index) {
return (E) elements[index];
}
主要缺点是:
1)内存占用:添加数据的时候,要额外创建一个数组变量来存放新的元素,比如就多出了一份内存。内存太大时可能造成频繁的full gc。考虑使用concurrentHashMap替换。
2)数据一致性:只能保证最终数据的一致,但是不能保证实时一致
详细讲解文章:Java并发编程:并发容器之CopyOnWriteArrayList
2、ConcurrentHashMap:采用锁分段技术,有一个segment数组,每个segment元素是一个可重入锁(继承自ReenTrantLock)。每个segment元素对应着一个hashEntry数组,每个hashEntry是个链表结构,每个segment守护者他对应的hashEntry数组,要操作这个数组必须先获得对应的segment的锁。相比于hashTable(把所有的方法都加入了同步锁)来说,性能上有所提升。
数据结构: