Lock继承体系

Lock接口

Lock接口诞生于JDK1.5,接口内部提供了最基本的加锁、释放锁方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public interface Lock {

// 直接加锁
void lock();

// 支持可中断的加锁
void lockInterruptibly() throws InterruptedException;

// 尝试一次加锁
boolean tryLock();

// 尝试一次加锁(支持超时停止阻塞)
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;

// 解锁
void unlock();

// 创建一个Condition(作用于线程通信,后面会讲)
Condition newCondition();

使用层面
既然是接口,就是提供给开发者实现用的,Java自带了ReentrantLock、Condition、ReentrantReadWriteLock实现类供开发者使用。如果这些类无法满足业务需求,开发者可以通过实现Lock接口并利用AQS框架,自己定义一个Lock的具体实现锁(是否公平、是否支持超时、是否支持重入等),从而提高锁的灵活性。

与synchronized区别
由于Lock可以自己定义是否公平、是否支持超时、是否支持重入等功能,相对于synchronized关键字来说可发挥的空间更多,也更灵活。但是Lock的加锁、释放锁需要开发者自己编写,如果考虑不周很可能造成死锁情况(最好在try中加锁,finally中释放锁),而synchronized由JVM实现,完全不需要担心这些情况。

ReentrantLock

ReentrantLock就是Java自带的Lock实现类,字面的意思就能看出来是一把可重入锁,并且功能几乎与synchronized相似,我们看看源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
public class ReentrantLock implements Lock, java.io.Serializable {

private final Sync sync;

// 定义一个顶级同步器(内部包含一个非公平加锁方法,一个释放锁方法)
abstract static class Sync extends AbstractQueuedSynchronizer {

private static final long serialVersionUID = -5179523762034025860L;

abstract void lock();

// 非公平方式尝试一次加锁
final boolean nonfairTryAcquire(int acquires) {

// 获取试图尝试加锁的线程
final Thread current = Thread.currentThread();

// 获取公共资源状态
int c = getState();

// 如果没其他线程持有锁,进行加锁
if (c == 0) {
// 加锁前并没有校验等待队列是否已经有节点在等待了,这个if完全体现了非公平性
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}

// 如果有线程持有锁并且是自身,重入次数递增
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}

// 到这里说明锁被其他线程占了,直接返回false
return false;
}

// 释放锁
protected final boolean tryRelease(int releases) {

// 计算递减后的重入次数
int c = getState() - releases;

// 如果释放锁线程不是持有锁线程,抛异常(一般能执行这方法的都是持有锁线程)
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();

// 如果递减后为0,那就是真的释放锁了,清空自己的独占状态并返回
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}

// 返回调用此方法的线程是否持有锁
protected final boolean isHeldExclusively() {
return getExclusiveOwnerThread() == Thread.currentThread();
}

// 其他方法...
}

// 定义一个非公平同步器,继承顶级同步器
static final class NonfairSync extends Sync {

private static final long serialVersionUID = 7316153563782823691L;

// 实现顶级同步器的lock加锁方法
final void lock() {

// 尝试CAS 如果成功说明之前没线程加锁
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());

// 失败就在尝试获取一次,这里调用AQS的acquire()方法,
// AQS的acquire()方法又调用下面重写的tryAcquire方法
else
acquire(1);
}

// 绕了一大圈,其实就是用非公平锁方式加锁
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}

// 定义一个公平同步器,继承顶级同步器
static final class FairSync extends Sync {

private static final long serialVersionUID = -3000897897090466540L;

final void lock() {
acquire(1);
}

protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
}

// 无参构造器,默认使用非公平锁
public ReentrantLock() {
sync = new NonfairSync();
}

// 参数构造器,自行选择是否公平
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}

// 加锁
public void lock() {
sync.lock();
}

// 支持可中断加锁
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}

// 尝试一次加锁
public boolean tryLock() {
return sync.nonfairTryAcquire(1);
}

// 支持超时的加锁
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}

// 解锁
public void unlock() {
sync.release(1);
}

// 其他方法....
}

重入支持
ReentrantLock通过state属性控制重入,每次重入state+1、退出state-1,为0时代表释放锁。

是否公平锁

ReentrantLock类支持公平锁与非公平锁,并根据构造器初始化一个Sync(公平锁创建FairSync,非公平锁创建NonfairSync),后续加锁释放锁等操作完全调用Sync实现。FairSync与NonfairSync除了加锁逻辑不一样,其他的逻辑(比如释放锁等)完全一样。

ReentrantLock加锁是使用Sync的lock()实现,公平锁(FairSync)是直接调用AQS的acquire()方法获取锁,然后调用重写的tryAcquire()方法。在重写方法里面如果可以加锁(state=0),会先判断等待队列是否有元素在等待,如果没有元素可以直接加锁,如果加锁失败或存在元素,则加入等待队列尾部等待(按顺序排队)。

非公平锁(NonFairSync)在调用Sync的lock()方法时,只要可以加锁(state=0),会直接使用CAS进行加锁(无视等待队列是否有元素),如果插队失败了在调用AQS的acquire()再次加锁,重写的tryAcquire()方法还是会再次尝试插队,如果还是失败才会加入等待队列,因此非公平锁存在2次插队的操作。

ReadWriteLock接口

没啥好写的

ReentrantReadWriteLock

没啥好写的

评论