p.casNext(null, newNode)方法用于将入队节点设置为当前队列尾节点的next节点,如果p是null,
表示p是当前队列的尾节点,如果不为null,表示有其他线程更新了尾节点,则需要重新获取当前队列的尾节点
3.更新尾节点
casTail(t, newNode);
将尾节点移到最后(即把tail指向新节点)
如果失败了,那么说明有其他线程已经把tail移动过,此时新节点newNode为尾节点,tail为其前驱结点
poll()
public E poll() {
// 设置起始点
restartFromHead:
for (;;) {
for (Node<E> h = head, p = h, q;;) {
E item = p.item;
// 利用cas 将第一个节点设置为null
if (item != null && p.casItem(item, null)) {
// 和上面类似,p的next被删了,
// 然后然后判断一下,目的为了保证head的next不为空
if (p != h) // hop two nodes at a time
updateHead(h, ((q = p.next) != null) ? q : p);
return item;
}
else if ((q = p.next) == null) {
// 有可能已经被另外线程先删除了下一个节点
// 那么需要先设定head 的位置,并返回null
updateHead(h, p);
return null;
}
else if (p == q)
continue restartFromHead;
else
// 和offer 类似,保证下一个节点有值,才能删除
p = q;
}
}
}
ConcurrentHashMap(并发的HashMap)
JDK1.7与JDK1.8 ConcurrentHashMap的实现还是有不小的区别的
JDK1.7
在JDK1.7版本中,ConcurrentHashMap的数据结构是由一个Segment数组和多个HashEntry组成。
Segment数组的意义就是将一个大的table分割成多个小的table来进行加锁,也就是上面的提到的锁分离技术,而每一个Segment元素存储的是HashEntry数组+链表,这个和HashMap的数据存储结构一样.


JDK1.8
1.8中放弃了Segment臃肿的设计,取而代之的是采用Node + CAS + Synchronized来保证并发安全进行实现,结构如下:


put实现
public V put(K key, V value) {
return putVal(key, value, false);
}
/** Implementation for put and putIfAbsent */
final V putVal(K key, V value, boolean onlyIfAbsent) {
if (key == null || value == null) throw new NullPointerException();
int hash = spread(key.hashCode());
int binCount = 0;
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; int n, i, fh;
if (tab == null || (n = tab.length) == 0)
//① 只有在执行第一次put方法时才会调用initTable()初始化Node数组
tab = initTable();
//② 如果相应位置的Node还未初始化,则通过CAS插入相应的数据;
else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
if (casTabAt(tab, i, null,
new Node<K,V>(hash, key, value, null)))
break;
// no lock when adding to empty bin
}
//③ 如果相应位置的Node不为空,且当前该节点处于移动状态 帮助转移数据
else if ((fh = f.hash) == MOVED)
tab = helpTransfer(tab, f);
//④ 如果相应位置的Node不为空,且当前该节点不处于移动状态,则对该节点加synchronized锁,
else {
V oldVal = null;
synchronized (f) {
if (tabAt(tab, i) == f) {
//⑤ 如果该节点的hash不小于0,则遍历链表更新节点或插入新节点;
if (fh >= 0) {
binCount = 1;
for (Node<K,V> e = f;; ++binCount) {
K ek;
if (e.hash == hash &&
((ek = e.key) == key ||
(ek != null && key.equals(ek)))) {
oldVal = e.val;
if (!onlyIfAbsent)
e.val = value;
break;
}
Node<K,V> pred = e;
if ((e = e.next) == null) {
pred.next = new Node<K,V>(hash, key,
value, null);
break;
}
}
}
//⑥ 如果该节点是TreeBin类型的节点,说明是红黑树结构,则通过putTreeVal方法往红黑树中插入节点;
else if (f instanceof TreeBin) {
Node<K,V> p;
binCount = 2;
if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,
value)) != null) {
oldVal = p.val;
if (!onlyIfAbsent)
p.val = value;
}
}
}
}
/**
*如果binCount不为0,说明put操作对数据产生了影响,如果当前链表的个数达到8个,
*通过treeifyBin方法转化为红黑树,
*如果oldVal不为空,说明是一次更新操作,没有对元素个数产生影响,则直接返回旧值;
*/
if (binCount != 0) {
if (binCount >= TREEIFY_THRESHOLD)
treeifyBin(tab, i);
if (oldVal != null)
return oldVal;
break;
}
}
}
addCount(1L, binCount);
return null;
}
CopyOnWriteArrayList(线程安全的ArrayList)
JDK1.8中关于CopyOnWriteArrayList的官方介绍如下
A thread-safe variant of {@link java.util.ArrayList} in which all mutative
operations ({@code add}, {@code set}, and so on)
are implemented bymaking a fresh copy of the underlying array.
中文翻译大致是
CopyOnWriteArrayList是一个线程安全的java.util.ArrayList的变体,
add,set等改变CopyOnWriteArrayList的操作是通过制作当前数据的副本实现的
其实意思很简单,假设有一个数组如下所示

并发读取
多个线程并发读取是没有任何问题的

更新数组