p.casNext(null, newNode)方法用于将入队节点设置为当前队列尾节点的next节点,如果p是null,
表示p是当前队列的尾节点,如果不为null,表示有其他线程更新了尾节点,则需要重新获取当前队列的尾节点
3.更新尾节点
casTail(t, newNode);
将尾节点移到最后(即把tail指向新节点)
如果失败了,那么说明有其他线程已经把tail移动过,此时新节点newNode为尾节点,tail为其前驱结点
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的操作是通过制作当前数据的副本实现的
其实意思很简单,假设有一个数组如下所示
多个线程并发读取是没有任何问题的
更新数组