CopyOnWriteArrayList和CopyOnWriteArraySet
CopyOnWrite(COW)即写时复制,在进行读操作时不加锁以保证性能不受影响,进行写操作时加锁,复制资源的一份副本,在副本上执行写操作,写操作完成后将资源的引用指向副本。高并发环境下,当读操作次数远远大于写操作次数时这种做法可以大大提高读操作的效率。
- 它最适合于具有以下特征的应用程序:大小很小,只读操作远多于可变操作,需要在遍历期间防止线程间的冲突。
- 它是线程安全的。
- 因为通常需要复制整个基础数组,所以可变操作(add()、set() 和 remove() 等等)的开销很大。
- 迭代器支持hasNext(), next()等不可变操作,但不支持可变 remove()等操作。
- 使用迭代器进行遍历的速度很快,并且不会与其他线程发生冲突。在构造迭代器时,迭代器依赖于不变的数组快照。
JUC容器中List的实现只有CopyOnWriteArrayList,相当于线程安全的ArrayList,和ArrayList一样,它是个可变数组。
//写操作的线程安全性就是依赖ReentrantLock实现的。
final transient ReentrantLock lock = new ReentrantLock();
//底层是volatile数组
private transient volatile Object[] array;
//可以看出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();
}
}
CopyOnWriteArraySet相当于线程安全的HashSet,是依赖于CopyOnWriteArrayList实现的,但是CopyOnWriteArraySet是一个Set集合它不能有重复集合,所以CopyOnWriteArrayList额外提供了addIfAbsent()和addAllAbsent()这两个添加元素的API,通过这些API来添加元素时,只有当元素不存在时才执行添加操作。
ConcurrentLinkedQueue
ConcurrentLinkedQueue是一个线程安全的基于单向链表实现的无界的队列。队列元素中不可以放置null元素(内部实现的特殊节点除外),按照 FIFO(先进先出)原则对元素进行排序,是通过CAS来实现线程安全。
public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
implements Queue<E>, java.io.Serializable {
private static class Node<E> {
volatile E item;
volatile Node<E> next;
}
/**
* 以下表达式一直成立:
* - all live nodes are reachable from head via succ()
* - head != null
* - (tmp = head).next != tmp || tmp != head
* 以下表达式不一定成立:
* - head.item may or may not be null.
* - it is permitted for tail to lag behind head, that is, for tail
* to not be reachable from head!
*/
private transient volatile Node<E> head;
/**
* 只有tail的下一个节点一定是Null。
* 以下表达式一直成立:
* - the last node is always reachable from tail via succ()
* - tail != null
* 以下表达式不一定成立:
* - tail.item may or may not be null.
* - it is permitted for tail to lag behind head, that is, for tail
* to not be reachable from head!
* - tail.next may or may not be self-pointing to tail.
*/
private transient volatile Node<E> tail;
}
ConcurrentLinkedDeque
ConcurrentLinkedDeque是一个基于双向链表的无界双端队列,该队列同时支持FIFO和FILO两种操作方式,也是通过CAS来实现线程安全。
- 在多线程环境下head节点或者说链表随时可能被修改,每次循环的时候都需要重新获取。
public class ConcurrentLinkedDeque<E>
extends AbstractCollection<E>
implements Deque<E>, java.io.Serializable {
/**
* A node from which the first node on list (that is, the unique node p
* with p.prev == null && p.next != p) can be reached in O(1) time.
* Invariants:
* - the first node is always O(1) reachable from head via prev links
* - all live nodes are reachable from the first node via succ()
* - head != null
* - (tmp = head).next != tmp || tmp != head
* - head is never gc-unlinked (but may be unlinked)
* Non-invariants:
* - head.item may or may not be null
* - head may not be reachable from the first or last node, or from tail
*/
private transient volatile Node<E> head;
/**
* A node from which the last node on list (that is, the unique node p
* with p.next == null && p.prev != p) can be reached in O(1) time.
* Invariants:
* - the last node is always O(1) reachable from tail via next links
* - all live nodes are reachable from the last node via pred()
* - tail != null
* - tail is never gc-unlinked (but may be unlinked)
* Non-invariants:
* - tail.item may or may not be null
* - tail may not be reachable from the first or last node, or from head
*/
private transient volatile Node<E> tail;
private static final Node<Object> PREV_TERMINATOR, NEXT_TERMINATOR;
static final class Node<E> {
volatile Node<E> prev;
volatile E item;
volatile Node<E> next;
}
}