上篇:Java多线程(8)线程池
ReentrantLock原理
继承关系
- ReentrantLock 在内部用了内部类 Sync 来管理锁,所以真正的获取锁和释放锁是由 Sync 的实现类来控制的。
- Sync 有两个实现,分别为 NonfairSync(非公平锁)和 FairSync(公平锁)。
- 默认是非公平锁
public ReentrantLock() {
sync = new NonfairSync();
}
一. 加锁流程
假设有两个线程,Thread-0和Thread-1,竞争同一个对象锁
当Thread-0调用lock方法时:
sync.lock()的实现:
当Thread-0进来时(此时没有竞争):
加锁过程
使用cas原子操作尝试将对象锁的状态(state) 从 0 改变成 1
如果改变成功,则直接将对象的OwnerThread设置为Thread-0
当第Thread-1调用lock方法尝试获取锁(即出现竞争关系)
Thread-1尝试将对象锁的状态从0改变成1
但,此时锁的状态已经为1 所以这次cas操作会失败,然后他会走else下面的acquire方法
acquire方法源码:
这个时候Thread-1会再次去尝试获取这个锁:
这个时候有两种情况
- 尝试获取锁的期间,如果Thread-0线程放开了锁,那么他会立刻得到这把锁(原理也是将锁的状态改为1,OwnerThread设置为Thread-1)
- 尝试获取锁的期间,还是加锁失败了,那么他会执行addWaiter方法
进入addWaiter,Thread-1会构造一个Node(结构为双向链表)队列
- Node的创建是懒惰的
- 第一个Node成为Dummy(哑元)或哨兵,用来占位不关联线程
接下来Thread-1会执行acquire方法
进入shouldParkAfterFailedAcquire,发现前驱节点head的waitStatus不是SIGNAL,compareAndSetWaitStatus将head的waitStatus设置为-1
SIGNAL定义为-1
设置完成之后,回到 acquireQueued方法,for无限循环最后Thread-1还是再次进入shouldParkAfterFailedAcquire
Thread-1再次进入 shouldParkAfterFailedAcquire 时,这时因为其前驱 node 的 waitStatus 已经是 -1,这次返回true
返回true以后,进入 parkAndCheckInterrupt
Thread-1 park(灰色表示)
若是多个线程经历上述失败过程:
二. 释放锁 unlock
进入tryRelease,若成功:
- 设置 exclusiveOwnerThread 为 null
- state = 0
三. 释放成功竞争锁
进入 unparkSuccessor 流程
找到队列中离 head 最近的一个 Node(没取消的),unpark 恢复其运行,本例中的最近node是Thread-1
那么 Thread-1 回到 acquireQueued方法
如果这个尝试加锁成功(没有线程竞争或者为公平锁的情况下)那么:
- exclusiveOwnerThread 为 Thread-1,state = 1
- head 指向刚刚 Thread-1 所在的 Node,该 Node 清空 Thread
- 原本的 head 因为从链表断开,而可被垃圾回收
如果这时候有其它线程来竞争(非公平的体现),例如这时有 Thread-4 来了
如果不巧又被 Thread-4 占了先
- Thread-4 被设置为 exclusiveOwnerThread,state = 1
- Thread-1 再次进入 acquireQueued 流程,获取锁失败,重新进入 park 阻塞
四. 锁重入
当对象有锁的的时候,他会进入这个判断,如果当前线程等于加锁的线程那么他的state 就会++
解锁时:
每一次释放会让state -1只有等 state==0的时候才能释放锁
五. 公平锁
// 与非公平锁主要区别在于 tryAcquire 方法的实现
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
// 先检查 AQS 队列中是否有前驱节点, 没有才去竞争
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;
}