AQS原理分析
什么是AQS
java.util.concurrent包中的大多数同步器实现都是围绕着共同的基础行为,比如等待队
列、条件队列、独占获取、共享获取等,而这些行为的抽象就是基于
AbstractQueuedSynchronizer(简称AQS)
实现的,AQS是一个抽象同步框架,可以用来实
现一个依赖状态的同步器。
JDK中提供的大多数的同步器如Lock, Latch, Barrier等,都是基于AQS框架来实现的
一般是通过一个内部类Sync继承 AQS
将同步器所有调用都映射到Sync对应的方法
AQS具备的特性:
阻塞等待队列
共享/独占
公平/非公平
可重入
允许中断
AQS内部维护属性
volatile int state
state表示资源的可用状态
State三种访问方式:
getState()
setState()
compareAndSetState()
AQS定义两种资源共享方式
Exclusive-独占,只有一个线程能执行,如ReentrantLock
Share-共享,多个线程可以同时执行,如Semaphore/CountDownLatch
AQS定义两种队列
同步等待队列:
主要用于维护获取锁失败时入队的线程
条件等待队列:
调用await()的时候会释放锁,然后线程会加入到条件队列,调用
signal()唤醒的时候会把条件队列中的线程节点移动到同步队列中,等待再次获得锁
AQS 定义了5个队列中节点状态:
1. 值为0,初始化状态,表示当前节点在sync队列中,等待着获取锁。
2. CANCELLED,值为1,表示当前的线程被取消;
3. SIGNAL,值为-1,表示当前节点的后继节点包含的线程需要运行,也就是unpark;
4. CONDITION,值为-2,表示当前节点在等待condition,也就是在condition队列
中;
5. PROPAGATE,值为-3,表示当前场景下后续的acquireShared能够得以执行;
不同的自定义同步器竞争共享资源的方式也不同。自定义同步器在实现时只需要实现共享
资源state的获取与释放方式即可,至于具体线程等待队列的维护(如获取资源失败入队/唤醒出
队等),AQS已经在顶层实现好了
。自定义同步器实现时主要实现以下几种方法:
isHeldExclusively():该线程是否正在独占资源。只有用到condition才需要去实现
它。
tryAcquire(int):独占方式。
尝试获取资源,成功则返回true,失败则返回false。
tryRelease(int):独占方式。
尝试释放资源,成功则返回true,失败则返回false。
tryAcquireShared(int):共享方式。
尝试获取资源。负数表示失败;0表示成功,但
没有剩余可用资源;正数表示成功,且有剩余资源。
tryReleaseShared(int):共享方式。
尝试释放资源,如果释放后允许唤醒后续等待
结点返回true,否则返回false。
同步等待队列
AQS当中的同步等待队列也称CLH队列,CLH队列是Craig、Landin、Hagersten三人发明
的一种
基于双向链表数据结构的队列
,
是FIFO先进先出线程等待队列
,Java中的CLH队列是原
CLH队列的一个变种,线程由原自旋机制改为阻塞机制。
AQS 依赖CLH同步队列来完成同步状态的管理:
当前线程如果获取同步状态失败时,AQS则会将当前线程已经等待状态等信息构造
成一个节点(Node)并将其加入到CLH同步队列,同时会阻塞当前线程
当同步状态释放时,会把首节点唤醒(公平锁),使其再次尝试获取同步状态。
通过signal或signalAll将条件队列中的节点转移到同步队列。(
由条件队列转化为同
步队列
)
条件等待队列
AQS中条件队列是使用单向列表保存的,用nextWaiter来连接:
调用await方法阻塞线程;
当前线程存在于同步队列的头结点,调用await方法进行阻塞(
从同步队列转化到条
件队列
)
Condition接口详解

1. 调用Condition#await方法
会释放当前持有的锁,然后阻塞当前线程,同时向
Condition队列尾部添加一个节点
,所以调用Condition#await方法的时候必须持有锁。
2. 调用Condition#signal方法会
将Condition队列的首节点移动到阻塞队列尾部,然后唤
醒因调用Condition#await方法而阻塞的线程
(唤醒之后这个线程就可以去竞争锁了),所
以调用Condition#signal方法的时候必须持有锁,持有锁的线程唤醒被因调用
Condition#await方法而阻塞的线程。
等待唤醒机制之await/signal测试
1
@Slf4j
2
public class
ConditionTest
{
3
4
public static void
main
(
String
[]
args
) {
5
6
Lock lock
=
new
ReentrantLock
();
7
Condition condition
=
lock
.
newCondition
();
8
9
new
Thread
(()
‐>
{
10
lock
.
lock
();
11
try
{
12
log
.
debug
(
Thread
.
currentThread
().
getName
()
+
"
开始处理任务
"
);
13
condition
.
await
();
14
log
.
debug
(
Thread
.
currentThread
().
getName
()
+
"
结束处理任务
"
);
15
}
catch
(
InterruptedException e
) {
16
e
.
printStackTrace
();
17
}
finally
{
18
lock
.
unlock
();
19
}
20
}).
start
();
21
22
new
Thread
(()
‐>
{
23
lock
.
lock
();
24
try
{
25
log
.
debug
(
Thread
.
currentThread
().
getName
()
+
"
开始处理任务
"
);
26
27
Thread
.
sleep
(
2000
);
28
condition
.
signal
();
29
log
.
debug
(
Thread
.
currentThread
().
getName
()
+
"
结束处理任务
"
);
30
}
catch
(
Exception e
) {
31
e
.
printStackTrace
();
32
}
finally
{
33
lock
.
unlock
();
34
}
35
}).
start
();
36
}
37
}
ReentrantLock详解
ReentrantLock是一种基于AQS框架的应用实现
,是JDK中的一种线程并发访问的同步手
段,它的功能类似于synchronized
是一种互斥锁,可以保证线程安全
。
相对于 synchronized, ReentrantLock具备如下特点:
可中断
可以设置超时时间
可以设置为公平锁
支持多个条件变量
与 synchronized 一样,都支持可重入

顺便总结了几点synchronized和ReentrantLock的区别:
synchronized是JVM层次的锁实现,ReentrantLock是JDK层次的锁实现;
synchronized的锁状态是无法在代码中直接判断的,但是ReentrantLock可以通过
ReentrantLock#isLocked判断;
synchronized是非公平锁,ReentrantLock是可以是公平也可以是非公平的;
synchronized是不可以被中断的,而ReentrantLock#lockInterruptibly方法是可以
被中断的;
在发生异常时synchronized会自动释放锁,而ReentrantLock需要开发者在finally
块中显示释放锁;
ReentrantLock获取锁的形式有多种:如立即返回是否成功的tryLock(),以及等待指
定时长的获取,更加灵活;
synchronized在特定的情况下对于已经在等待的线程是后来的线程先获得锁(回顾
一下sychronized的唤醒策略),而ReentrantLock对于已经在等待的线程是先来的线程
先获得锁;
ReentrantLock
的使用
同步执行,类似于synchronized
1
ReentrantLock lock
=
new
ReentrantLock
();
//
参数默认
false
,不公平锁
2
ReentrantLock lock
=
new
ReentrantLock
(
true
);
//
公平锁
3
4
//
加锁
5
lock
.
lock
();
6
try
{
7
//
临界区
8
}
finally
{
9
//
解锁
10
lock
.
unlock
();
11
}
测试
1
public class
ReentrantLockDemo
{
2
3
private static
int sum
=
0
;
4
private static
Lock lock
=
new
ReentrantLock
();
5
6
public static void
main
(
String
[]
args
)
throws InterruptedException
{
7
8
for
(
int i
=
0
;
i
<
3
;
i
++
) {
9
Thread thread
=
new
Thread
(()
‐>
{
10
//
加锁
11
lock
.
lock
();
12
try
{
13
for
(
int j
=
0
;
j
<
10000
;
j
++
) {
14
sum
++
;
15
}
16
}
finally
{
17
//
解锁
18
lock
.
unlock
();
19
}
20
});
21
thread
.
start
();
22
}
23
Thread
.
sleep
(
2000
);
24
System
.
out
.
println
(
sum
);
25
}
26
}
可重入
1
@Slf4j
2
public class
ReentrantLockDemo2
{
3
4
public static
ReentrantLock lock
=
new
ReentrantLock
();
5
6
public static void
main
(
String
[]
args
) {
7
method1
();
8
}
9
10
11
public static void
method1
() {
12
lock
.
lock
();
13
try
{
14
log
.
debug
(
"execute method1"
);
15
method2
();
16
}
finally
{
17
lock
.
unlock
();
18
}
19
}
20
public static void
method2
() {
21
lock
.
lock
();
22
try
{
23
log
.
debug
(
"execute method2"
);
24
method3
();
25
}
finally
{
26
lock
.
unlock
();
27
}
28
}
29
public static void
method3
() {
30
lock
.
lock
();
31
try
{
32
log
.
debug
(
"execute method3"
);
33
}
finally
{
34
lock
.
unlock
();
35
}
36
}
37
}
可中断
1
@Slf4j
2
public class
ReentrantLockDemo3
{
3
4
public static void
main
(
String
[]
args
) {
5
ReentrantLock lock
=
new
ReentrantLock
();
6
7
Thread t1
=
new
Thread
(()
‐>
{
8
9
log
.
debug
(
"t1
启动
..."
);
10
11
try
{
12
lock
.
lockInterruptibly
();
13
try
{
14
log
.
debug
(
"t1
获得了锁
"
);
15
}
finally
{
16
lock
.
unlock
();
17
}
18
}
catch
(
InterruptedException e
) {
19
e
.
printStackTrace
();
20
log
.
debug
(
"t1
等锁的过程中被中断
"
);
21
}
22
23
},
"t1"
);
24
25
lock
.
lock
();
26
try
{
27
log
.
debug
(
"main
线程获得了锁
"
);
28
t1
.
start
();
29
//
先让线程
t1
执行
30
try
{
31
Thread
.
sleep
(
1000
);
32
}
catch
(
InterruptedException e
) {
33
e
.
printStackTrace
();
34
}
35
t1
.
interrupt
();
36
log
.
debug
(
"
线程
t1
执行中断
"
);
37
}
finally
{
38
lock
.
unlock
();
39
}
40
}
41
}
锁超时
立即失败
1
@Slf4j
2
public class
ReentrantLockDemo4
{
3
4
public static void
main
(
String
[]
args
) {
5
ReentrantLock lock
=
new
ReentrantLock
();
6
7
Thread t1
=
new
Thread
(()
‐>
{
8
9
log
.
debug
(
"t1
启动
..."
);
10
//
注意: 即使是设置的公平锁,此方法也会立即返回获取锁成功或失败,公平策略不生效
11
if
(
!
lock
.
tryLock
()) {
12
log
.
debug
(
"t1
获取锁失败,立即返回
false"
);
13
return
;
14
}
15
try
{
16
log
.
debug
(
"t1
获得了锁
"
);
17
}
finally
{
18
lock
.
unlock
();
19
}
20
21
},
"t1"
);
22
23
24
lock
.
lock
();
25
try
{
26
log
.
debug
(
"main
线程获得了锁
"
);
27
t1
.
start
();
28
//
先让线程
t1
执行
29
try
{
30
Thread
.
sleep
(
1000
);
31
}
catch
(
InterruptedException e
) {
32
e
.
printStackTrace
();
33
}
34
}
finally
{
35
lock
.
unlock
();
36
}
37
38
}
39
40
}
超时失败
1
@Slf4j
2
public class
ReentrantLockDemo4
{
3
4
public static void
main
(
String
[]
args
) {
5
ReentrantLock lock
=
new
ReentrantLock
();
6
Thread t1
=
new
Thread
(()
‐>
{
7
log
.
debug
(
"t1
启动
..."
);
8
//
超时
9
try
{
10
if
(
!
lock
.
tryLock
(
1
,
TimeUnit
.
SECONDS
)) {
11
log
.
debug
(
"
等待
1s
后获取锁失败,返回
"
);
12
return
;
13
}
14
}
catch
(
InterruptedException e
) {
15
e
.
printStackTrace
();
16
return
;
17
}
18
try
{
19
log
.
debug
(
"t1
获得了锁
"
);
20
}
finally
{
21
lock
.
unlock
();
22
}
23
24
},
"t1"
);
25
26
27
lock
.
lock
();
28
try
{
29
log
.
debug
(
"main
线程获得了锁
"
);
30
t1
.
start
();
31
//
先让线程
t1
执行
32
try
{
33
Thread
.
sleep
(
2000
);
34
}
catch
(
InterruptedException e
) {
35
e
.
printStackTrace
();
36
}
37
}
finally
{
38
lock
.
unlock
();
39
}
40
41
}
42
43
}
公平锁
ReentrantLock 默认是不公平的
1
@Slf4j
2
public class
ReentrantLockDemo5
{
3
4
public static void
main
(
String
[]
args
)
throws InterruptedException
{
5
ReentrantLock lock
=
new
ReentrantLock
(
true
);
//
公平锁
6
7
for
(
int i
=
0
;
i
<
500
;
i
++
) {
8
new
Thread
(()
‐>
{
9
lock
.
lock
();
10
try
{
11
try
{
12
Thread
.
sleep
(
10
);
13
}
catch
(
InterruptedException e
) {
14
e
.
printStackTrace
();
15
}
16
log
.
debug
(
Thread
.
currentThread
().
getName
()
+
" running..."
);
17
}
finally
{
18
lock
.
unlock
();
19
}
20
},
"t"
+
i
).
start
();
21
}
22
// 1s
之后去争抢锁
23
Thread
.
sleep
(
1000
);
24
25
for
(
int i
=
0
;
i
<
500
;
i
++
) {
26
new
Thread
(()
‐>
{
27
lock
.
lock
();
28
try
{
29
log
.
debug
(
Thread
.
currentThread
().
getName
()
+
" running..."
);
30
}
finally
{
31
lock
.
unlock
();
32
}
33
},
"
强行插入
"
+
i
).
start
();
34
}
35
}
36
}
思考:
ReentrantLock公平锁和非公平锁的性能谁更高?
条件变量
java.util.concurrent类库中提供Condition类来实现线程之间的协调。调用
Condition.await() 方法使线程等待,其他线程调用Condition.signal() 或
Condition.signalAll() 方法唤醒等待的线程。
注意:
调用Condition的await()和signal()方法,都必须在lock保护之内。
1
@Slf4j
2
public class
ReentrantLockDemo6
{
3
private static
ReentrantLock lock
=
new
ReentrantLock
();
4
private static
Condition cigCon
=
lock
.
newCondition
();
5
private static
Condition takeCon
=
lock
.
newCondition
();
6
7
private static
boolean hashcig
=
false
;
8
private static
boolean hastakeout
=
false
;
9
10
//
送烟
11
public void
cigratee
(){
12
lock
.
lock
();
13
try
{
14
while
(
!
hashcig
){
15
try
{
16
log
.
debug
(
"
没有烟,歇一会
"
);
17
cigCon
.
await
();
18
19
}
catch
(
Exception e
){
20
e
.
printStackTrace
();
21
}
22
}
23
log
.
debug
(
"
有烟了,干活
"
);
24
}
finally
{
25
lock
.
unlock
();
26
}
27
}
28
29
//
送外卖
30
public void
takeout
(){
31
lock
.
lock
();
32
try
{
33
while
(
!
hastakeout
){
34
try
{
35
log
.
debug
(
"
没有饭,歇一会
"
);
36
takeCon
.
await
();
37
38
}
catch
(
Exception e
){
39
e
.
printStackTrace
();
40
}
41
}
42
log
.
debug
(
"
有饭了,干活
"
);
43
}
finally
{
44
lock
.
unlock
();
45
}
46
}
47
48
public static void
main
(
String
[]
args
) {
49
ReentrantLockDemo6 test
=
new
ReentrantLockDemo6
();
50
new
Thread
(()
‐>
{
51
test
.
cigratee
();
52
}).
start
();
53
54
new
Thread
(()
‐>
{
55
test
.
takeout
();
56
}).
start
();
57
58
new
Thread
(()
‐>
{
59
lock
.
lock
();
60
try
{
61
hashcig
=
true
;
62
//
唤醒送烟的等待线程
63
cigCon
.
signal
();
64
}
finally
{
65
lock
.
unlock
();
66
}
67
68
69
},
"t1"
).
start
();
70
71
new
Thread
(()
‐>
{
72
lock
.
lock
();
73
try
{
74
hastakeout
=
true
;
75
//
唤醒送饭的等待线程
76
takeCon
.
signal
();
77
}
finally
{
78
lock
.
unlock
();
79
}
80
},
"t2"
).
start
();
81
}
82
83
}