Skip to content

Commit 9f92931

Browse files
整理部分代码
1 parent 9ce3a58 commit 9f92931

File tree

23 files changed

+3555
-3488
lines changed

23 files changed

+3555
-3488
lines changed

.idea/.gitignore

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/concurrent.iml

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/deployment.xml

Lines changed: 14 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/modules.xml

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/vcs.xml

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

09.深入理解AbstractQueuedSynchronizer(AQS)/深入理解AbstractQueuedSynchronizer(AQS).md

Lines changed: 430 additions & 411 deletions
Large diffs are not rendered by default.

10.彻底理解ReentrantLock/彻底理解ReentrantLock.md

Lines changed: 75 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -5,87 +5,88 @@ ReentrantLock重入锁,是实现Lock接口的一个类,也是在实际编程
55

66
# 2. 重入性的实现原理 #
77

8-
要想支持重入性,就要解决两个问题:**1. 在线程获取锁的时候,如果已经获取锁的线程是当前线程的话则直接再次获取成功;2. 由于锁会被获取n次,那么只有锁在被释放同样的n次之后,该锁才算是完全释放成功。**通过[这篇文章](https://juejin.im/post/5aeb07ab6fb9a07ac36350c8),我们知道,同步组件主要是通过重写AQS的几个protected方法来表达自己的同步语义。针对第一个问题,我们来看看ReentrantLock是怎样实现的,以非公平锁为例,判断当前线程能否获得锁为例,核心方法为nonfairTryAcquire:
9-
10-
11-
final boolean nonfairTryAcquire(int acquires) {
12-
final Thread current = Thread.currentThread();
13-
int c = getState();
14-
//1. 如果该锁未被任何线程占有,该锁能被当前线程获取
15-
if (c == 0) {
16-
if (compareAndSetState(0, acquires)) {
17-
setExclusiveOwnerThread(current);
18-
return true;
19-
}
20-
}
21-
//2.若被占有,检查占有线程是否是当前线程
22-
else if (current == getExclusiveOwnerThread()) {
23-
// 3. 再次获取,计数加一
24-
int nextc = c + acquires;
25-
if (nextc < 0) // overflow
26-
throw new Error("Maximum lock count exceeded");
27-
setState(nextc);
28-
return true;
29-
}
30-
return false;
31-
}
32-
8+
要想支持重入性,就要解决两个问题:**1. 在线程获取锁的时候,如果已经获取锁的线程是当前线程的话则直接再次获取成功;2. 由于锁会被获取n次,那么只有锁在被释放同样的n次之后,该锁才算是完全释放成功。**
9+
通过[这篇文章](https://juejin.im/post/5aeb07ab6fb9a07ac36350c8),我们知道,同步组件主要是通过重写AQS的几个protected方法来表达自己的同步语义。针对第一个问题,我们来看看ReentrantLock是怎样实现的,以非公平锁为例,判断当前线程能否获得锁为例,核心方法为nonfairTryAcquire:
10+
11+
```java
12+
final boolean nonfairTryAcquire(int acquires) {
13+
final Thread current = Thread.currentThread();
14+
int c = getState();
15+
//1. 如果该锁未被任何线程占有,该锁能被当前线程获取
16+
if (c == 0) {
17+
if (compareAndSetState(0, acquires)) {
18+
setExclusiveOwnerThread(current);
19+
return true;
20+
}
21+
}
22+
//2.若被占有,检查占有线程是否是当前线程
23+
else if (current == getExclusiveOwnerThread()) {
24+
// 3. 再次获取,计数加一
25+
int nextc = c + acquires;
26+
if (nextc < 0) // overflow
27+
throw new Error("Maximum lock count exceeded");
28+
setState(nextc);
29+
return true;
30+
}
31+
return false;
32+
}
33+
```
3334
这段代码的逻辑也很简单,具体请看注释。为了支持重入性,在第二步增加了处理逻辑,如果该锁已经被线程所占有了,会继续检查占有线程是否为当前线程,如果是的话,同步状态加1返回true,表示可以再次获取成功。每次重新获取都会对同步状态进行加一的操作,那么释放的时候处理思路是怎样的了?(依然还是以非公平锁为例)核心方法为tryRelease:
34-
35-
protected final boolean tryRelease(int releases) {
36-
//1. 同步状态减1
37-
int c = getState() - releases;
38-
if (Thread.currentThread() != getExclusiveOwnerThread())
39-
throw new IllegalMonitorStateException();
40-
boolean free = false;
41-
if (c == 0) {
42-
//2. 只有当同步状态为0时,锁成功被释放,返回true
43-
free = true;
44-
setExclusiveOwnerThread(null);
45-
}
46-
// 3. 锁未被完全释放,返回false
47-
setState(c);
48-
return free;
49-
}
50-
35+
```java
36+
protected final boolean tryRelease(int releases) {
37+
//1. 同步状态减1
38+
int c = getState() - releases;
39+
if (Thread.currentThread() != getExclusiveOwnerThread())
40+
throw new IllegalMonitorStateException();
41+
boolean free = false;
42+
if (c == 0) {
43+
//2. 只有当同步状态为0时,锁成功被释放,返回true
44+
free = true;
45+
setExclusiveOwnerThread(null);
46+
}
47+
// 3. 锁未被完全释放,返回false
48+
setState(c);
49+
return free;
50+
}
51+
```
5152
代码的逻辑请看注释,需要注意的是,重入锁的释放必须得等到同步状态为0时锁才算成功释放,否则锁仍未释放。如果锁被获取n次,释放了n-1次,该锁未完全释放返回false,只有被释放n次才算成功释放,返回true。到现在我们可以理清ReentrantLock重入性的实现了,也就是理解了同步语义的第一条。
5253

5354
# 3. 公平锁与公平锁 #
5455
ReentrantLock支持两种锁:**公平锁****非公平锁****何谓公平性,是针对获取锁而言的,如果一个锁是公平的,那么锁的获取顺序就应该符合请求上的绝对时间顺序,满足FIFO**。ReentrantLock的构造方法无参时是构造非公平锁,源码为:
55-
56-
public ReentrantLock() {
57-
sync = new NonfairSync();
58-
}
59-
56+
```java
57+
public ReentrantLock() {
58+
sync = new NonfairSync();
59+
}
60+
```
6061
另外还提供了另外一种方式,可传入一个boolean值,true时为公平锁,false时为非公平锁,源码为:
61-
62-
public ReentrantLock(boolean fair) {
63-
sync = fair ? new FairSync() : new NonfairSync();
64-
}
65-
62+
```java
63+
public ReentrantLock(boolean fair) {
64+
sync = fair ? new FairSync() : new NonfairSync();
65+
}
66+
```
6667
在上面非公平锁获取时(nonfairTryAcquire方法)只是简单的获取了一下当前状态做了一些逻辑处理,并没有考虑到当前同步队列中线程等待的情况。我们来看看公平锁的处理逻辑是怎样的,核心方法为:
67-
68-
protected final boolean tryAcquire(int acquires) {
69-
final Thread current = Thread.currentThread();
70-
int c = getState();
71-
if (c == 0) {
72-
if (!hasQueuedPredecessors() &&
73-
compareAndSetState(0, acquires)) {
74-
setExclusiveOwnerThread(current);
75-
return true;
76-
}
77-
}
78-
else if (current == getExclusiveOwnerThread()) {
79-
int nextc = c + acquires;
80-
if (nextc < 0)
81-
throw new Error("Maximum lock count exceeded");
82-
setState(nextc);
83-
return true;
84-
}
85-
return false;
86-
}
87-
}
88-
68+
```java
69+
protected final boolean tryAcquire(int acquires) {
70+
final Thread current = Thread.currentThread();
71+
int c = getState();
72+
if (c == 0) {
73+
if (!hasQueuedPredecessors() &&
74+
compareAndSetState(0, acquires)) {
75+
setExclusiveOwnerThread(current);
76+
return true;
77+
}
78+
}
79+
else if (current == getExclusiveOwnerThread()) {
80+
int nextc = c + acquires;
81+
if (nextc < 0)
82+
throw new Error("Maximum lock count exceeded");
83+
setState(nextc);
84+
return true;
85+
}
86+
return false;
87+
}
88+
}
89+
```
8990
这段代码的逻辑与nonfairTryAcquire基本上一直,唯一的不同在于增加了hasQueuedPredecessors的逻辑判断,方法名就可知道该方法用来判断当前节点在同步队列中是否有前驱节点的判断,如果有前驱节点说明有线程比当前线程更早的请求资源,根据公平性,当前线程请求资源失败。如果当前节点没有前驱节点的话,再才有做后面的逻辑判断的必要性。**公平锁每次都是从同步队列中的第一个节点获取到锁,而非公平性锁则不一定,有可能刚释放锁的线程能再次获取到锁**
9091

9192
> **公平锁 VS 非公平锁**

0 commit comments

Comments
 (0)