Skip to content

Commit 9d648f6

Browse files
committed
[Conclusion]
1.Add conclusion about ConcurrentLinkedList.
1 parent b32cbe3 commit 9d648f6

File tree

1 file changed

+63
-5
lines changed

1 file changed

+63
-5
lines changed

DataStructrue/Queue/ConcurrentLinkedQueue.md

Lines changed: 63 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,20 @@
33
* 通过CAS实现无锁非阻塞
44
>基于链接节点的、无界的、线程安全。此队列按照 FIFO(先进先出)原则对元素进行排序。队列的头部 是队列中时间最长的元素。队列的尾部 是队列中时间最短的元素。新的元素插入到队列的尾部,队列检索操作从队列头部获得元素。当许多线程共享访问一个公共 collection 时,ConcurrentLinkedQueue 是一个恰当的选择。此队列不允许 null 元素。
55
6+
## Compare and swap(CAS)
7+
* CAS是一种替代锁存在的无锁并发机制。
8+
* 该种无锁机制也是乐观锁的体现。
9+
* Why need CAS:一个修改操作可以分为:从内存中读取值->修改值->写回内存,在高并发的过程中,这一个过程失去了其原子性,所以修改值的原始值可能已经被其他线程修改了。
10+
i != i
11+
这种操作在单线程中一定是true,在多线程中i的值可能在别的线程被更改。
12+
* 如何实现CAS:在循环中读取这次循环的内存中存储值,进行修改,在写回内存中时比较内存中的值是否还是第一步取出的值,不是的话说明这次循环失败,重新开始,是的话写入新的值。
13+
```Java
14+
private static final Unsafe unsafe = Unsafe.getUnsafe(); //JNI实现的Unsafe类。
15+
public final boolean compareAndSet(long expect, long update) {
16+
return unsafe.compareAndSwapLong(this, valueOffset, expect, update);
17+
}
18+
```
19+
620
## Node
721
* ConcurrentLinkedQueue的内部私有类
822
* 使用了通过C++编写的Unsafe类
@@ -17,12 +31,15 @@
1731
Node(E item) {
1832
UNSAFE.putObject(this, itemOffset, item);
1933
}
34+
//通过CAS保证了操作的原子性。
35+
//CAS应用:表示设置当前Node的item值。第一个参数为期望值,第二个参数为设置目标值。当当前值等于期望值时(就是没有被其他人改过),就会将目标设置为val。
2036
boolean casItem(E cmp, E val) {
2137
return UNSAFE.compareAndSwapObject(this, itemOffset, cmp, val);
2238
}
2339
void lazySetNext(Node<E> val) {
2440
UNSAFE.putOrderedObject(this, nextOffset, val);
2541
}
42+
//CAS 下一个结点
2643
boolean casNext(Node<E> cmp, Node<E> val) {
2744
return UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val);
2845
}
@@ -46,6 +63,7 @@
4663
```
4764

4865
## ConcurrentLinkedQueue's API
66+
### 插入元素
4967
* add() 通过offer方法添加元素。
5068
```Java
5169
public boolean add(E e) {
@@ -54,6 +72,8 @@
5472
```
5573

5674
* offer()
75+
![clq](https://i.imgur.com/QONSTB2.jpg)
76+
5777
```Java
5878
public boolean offer(E e) {
5979
checkNotNull(e);
@@ -62,26 +82,64 @@
6282
for (Node<E> t = tail, p = t;;) {
6383
Node<E> q = p.next;
6484
if (q == null) {
65-
// p is last node
66-
if (p.casNext(null, newNode)) {
85+
// 当尾结点就是指向链表的最后一个元素,尝试CAS新的结点
86+
if (p.casNext(null, newNode)) { //将新的结点添加到链表的底部
6787
// Successful CAS is the linearization point
6888
// for e to become an element of this queue,
6989
// and for newNode to become "live".
70-
if (p != t) // hop two nodes at a time
90+
if (p != t) // 如上图所示,如果当前p不是tail结点,就将新接入的结点当做尾结点。相当于尾结点一次性跳跃两个结点。
7191
casTail(t, newNode); // Failure is OK.
7292
return true;
7393
}
7494
// Lost CAS race to another thread; re-read next
7595
}
76-
else if (p == q)
96+
else if (p == q) //哨兵结点情况
7797
// We have fallen off list. If tail is unchanged, it
7898
// will also be off-list, in which case we need to
7999
// jump to head, from which all live nodes are always
80100
// reachable. Else the new tail is a better bet.
101+
//如果在读取中,t的值被更新了,“打赌”t被更新正确了,不然重新从头开始。
81102
p = (t != (t = tail)) ? t : head;
82-
else
103+
else //q不是最后一个结点
83104
// Check for tail updates after two hops.
105+
//如果在该操作过程中,t的值被更新了,我们“打赌”t的值被更新正确
106+
//不然我们将p指针向后移动一位,重新进入循环
84107
p = (p != t && t != (t = tail)) ? t : q;
85108
}
86109
}
110+
```
111+
[高效读写的队列:深度剖析ConcurrentLinkedQueue](https://blog.csdn.net/chenguibao/article/details/51773322)
112+
113+
### 出队列
114+
![cdl](https://i.imgur.com/G5Irgel.jpg)
115+
* poll()
116+
```Java
117+
public E poll() {
118+
restartFromHead:
119+
for (;;) {
120+
for (Node<E> h = head, p = h, q;;) {
121+
E item = p.item;
122+
//取出头元素的值,并将其CAS成null。
123+
if (item != null && p.casItem(item, null)) {
124+
// Successful CAS is the linearization point
125+
// for item to be removed from this queue.
126+
//"打赌",假设头结点head已经被更新,则沿用h,不然更新为下一个结点
127+
if (p != h) // hop two nodes at a time
128+
updateHead(h, ((q = p.next) != null) ? q : p);
129+
return item;
130+
}
131+
//当前结点为空,下一个结点也为空,队列为空,退出循环,返回null。
132+
else if ((q = p.next) == null) {
133+
updateHead(h, p);
134+
return null;
135+
}
136+
//遇到哨兵结点,从新从头开始。
137+
else if (p == q)
138+
continue restartFromHead;
139+
//此时q == p.next, 将指针向右移动一位。
140+
else
141+
p = q;
142+
}
143+
}
144+
}
87145
```

0 commit comments

Comments
 (0)