Skip to content

Commit 521a586

Browse files
committed
重入锁
1 parent 570a1c3 commit 521a586

File tree

1 file changed

+66
-1
lines changed

1 file changed

+66
-1
lines changed

MD/ReentrantLock.md

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
`ReentrantLock` 就是一个普通的类,它是基于 `AQS(AbstractQueuedSynchronizer)`来实现的。
66

7+
它是一个**重入锁**,一个线程获得了锁之后仍然可以**反复**的加锁,不会出现自己阻塞自己的情况。
8+
79
> `AQS``Java` 并发包里实现锁、同步的一个基础框架。
810
911

@@ -90,4 +92,67 @@ ReentrantLock 分为**公平锁**和**非公平锁**,可以通过构造方法
9092
return false;
9193
}
9294
}
93-
```
95+
```
96+
97+
首先会判断 `AQS` 中的 `state` 是否等于0,0 就表示当前没有其他线程获得锁,当前线程就可以尝试获取锁。
98+
99+
注意:尝试之前会利用 `hasQueuedPredecessors()` 方法来判断 AQS 的队列中中是否有其他线程,如果有则就不会尝试获取锁了(**这是公平锁特有的情况**)。
100+
101+
如果队列中没有线程就利用 CAS 来将 AQS 中的 state 修改为1,也就是获取锁,获取成功则将当前线程置为获得锁的独占线程(`setExclusiveOwnerThread(current)`)。
102+
103+
如果 `state` 大于 0 时,说明锁已经被获取了,则需要判断获取锁的线程是否为当前线程(`ReentrantLock` 支持重入),是则需要将 `state + 1`,并将值更新。
104+
105+
106+
#### 写入队列
107+
如果 `tryAcquire(arg)` 获取锁失败,则需要用 `acquireQueued(addWaiter(Node.EXCLUSIVE), arg)` 将当前线程写入队列中。
108+
109+
写入之前需要将当前线程包装为一个 `Node` 对象(`addWaiter(Node.EXCLUSIVE)`)。
110+
111+
> AQS 中的队列是由 Node 节点组成的双向链表实现的。
112+
113+
114+
包装代码:
115+
116+
```java
117+
private Node addWaiter(Node mode) {
118+
Node node = new Node(Thread.currentThread(), mode);
119+
// Try the fast path of enq; backup to full enq on failure
120+
Node pred = tail;
121+
if (pred != null) {
122+
node.prev = pred;
123+
if (compareAndSetTail(pred, node)) {
124+
pred.next = node;
125+
return node;
126+
}
127+
}
128+
enq(node);
129+
return node;
130+
}
131+
132+
```
133+
134+
首先判断队列是否为空,不为空时则将封装好的 `Node` 利用 `CAS` 写入队尾,如果出现并发写入失败就需要调用 `enq(node);` 来写入了。
135+
136+
```java
137+
private Node enq(final Node node) {
138+
for (;;) {
139+
Node t = tail;
140+
if (t == null) { // Must initialize
141+
if (compareAndSetHead(new Node()))
142+
tail = head;
143+
} else {
144+
node.prev = t;
145+
if (compareAndSetTail(t, node)) {
146+
t.next = node;
147+
return t;
148+
}
149+
}
150+
}
151+
}
152+
```
153+
154+
这个处理逻辑就相当于自旋加上 `CAS` 保证一定写入队列。
155+
156+
157+
158+

0 commit comments

Comments
 (0)