JDK容器大部分在java.util.concurrent包中:
- ConcurrentHashMap:线程安全的HashMap
- HashMap线程不安全主要体现在多线程环境下扩容时会导致数据丢失或死循环。
- 一个简单的方法是使用Collections.synchronizedMap(new HashMap()),这种方式底层原理是在HashMap之上利用synchronized封装一层线程同步机制,无论是对Map读写,都需要获取锁,会导致效率低下。
- ConcurrentHashMap通过减小锁粒度实现效率上的提升。其内部细分为若干个(一般16个)小HashMap,称为段。当发生put时不同段的数据可以并行操作。然而当需要获取全局类信息如size时需要取得所有段的锁(事实上会先尝试无锁获取,失败时再执行所有锁机制),在size频繁调用场景下效率反而不如Collections.synchronizedMap(new HashMap())。
- ConcurrentHashMap通过volatile实现了无锁读,当读取失败时会加锁重试。
- CopyOnWriteArrayList
- 适合读多写少场景
- 写时会通过Arrays.copyOf复制一份数据,完成后替换旧数组,该操作通过ReentrantLock加锁
- ConcurrentLinkedQueue
- ConcurrentLinkedQueue是当前高并发场景中性能最好的线程安全队列。
- 其底层通过cas实现无锁操作。
- tail节点并不总是指向末尾,而是两次插入一更新
- 当出队时,可能产生哨兵节点
- 抢占节点时,当tail被更新会将当前节点指向tail,以减少节点遍历
public boolean offer(E e) {
checkNotNull(e);
final Node<E> newNode = new Node<E>(e);
for (Node<E> t = tail, p = t;;) {
Node<E> q = p.next;
if (q == null) {
// p is last node
if (p.casNext(null, newNode)) {
// Successful CAS is the linearization point
// for e to become an element of this queue,
// and for newNode to become "live".
if (p != t) // hop two nodes at a time
casTail(t, newNode); // Failure is OK.
return true;
}
// Lost CAS race to another thread; re-read next
}
else if (p == q)
// We have fallen off list. If tail is unchanged, it
// will also be off-list, in which case we need to
// jump to head, from which all live nodes are always
// reachable. Else the new tail is a better bet.
p = (t != (t = tail)) ? t : head;
else
// Check for tail updates after two hops.
p = (p != t && t != (t = tail)) ? t : q;
}
}
- BlockingQueue
- 阻塞式队列,适合用于数据共享通道
- 主要实现有ArrayBlockingQueue、LinkedBlockingQueue
- ConcurrentSkipListMap
- 跳表主要用于数据快速查找
- 相比于平衡树更新时只需更新局部数据,高并发下可以通过细粒度锁提高效率
- 跳表定义了多层链表,高层链表是底层链表的子集
- 内部主要由Node、Index、HeadIndex组成