Lock
Lock是java.util.concurrent.locks包提供的一个锁操作接口,相比于synchronized只能定义实例域和Class域的代码块,该接口提供了更灵活的方法,在代码层面提供了锁的机制,笔者将会在另一篇文章说明sychronized和Lock的区别。我们来看看都定义了什么接口,下面提到的获取到锁的对象就是一个Lock实例,用同一个lock实例去获取锁,才能保证是同一把锁。
1.lock(); 获取锁,如果锁不可用,则当前线程将无法被线程调度,并且休眠直到获取到锁。
2.lockInterruptibly(); 可中断获取锁,当获取锁时,如果锁可用则直接返回;如果锁不可用,当前线程将无法被线程调度,并且休眠直到线程获取到锁或者线程被其他线程中断。
3.tryLock();尝试获取锁,如果获取到了则返回true,否则返回false,利用这个方法,用户可以在获取不到锁的时候进行其他操作,该方法获取锁时不会进行等待。
4.tryLock(long time, TimeUnit unit);同上,但增加了超时设置,在获取锁时,对尝试获取一定时间。
5.unlock();释放锁。每一次获取锁以后都必须要进行释放,建议放在finally块中执行。
6.newCondition();创建一个绑定在当前锁的Condition实例。
ReentrantLock
ReentrantLock是Lock的默认实现类,ReentrantLock内部定义了一个抽象类Sync,该抽象类继承了同步器AbstractQueuedSynchronizer,Sync内部实现了tryRelease(int releases)释放锁,同时通过两个子类非公平锁NonfairSync和公平锁FairSync实现lock和tryAcquire方法。ReentrantLock内部维护了一个私有锁Sync sync,通过构造方法ReentrantLock(boolean fair)来实例化这个私有变量为公平锁或者非公平锁,ReentrantLock的一系列api都依赖于这个私有锁来实现,先来看一下Sync。
Sync
Sync继承了AbstractQueuedSynchronizer,并重写了获取锁释放锁等方法,是一个基于AbstractQueuedSynchronizer的同步器。我们来看看API:
方法 | 描述 |
isLocked() | 锁的状态,如果当前状态不等于0,则表明锁被持有。 |
getHoldCount() | 获取持有该锁的线程数,如果该锁为排他锁,则他的状态即为持有数。 |
getOwner() | 获取该锁的持有线程。 |
newCondition() | 绑定一个ConditionObject |
isHeldExclusively() | 判断该锁是否是排他锁 |
tryRelease(int var1) | 释放一次同步器,当前线程所持有的同步器必须是排他的,否则将抛出异常 |
nonfairTryAcquire(int var1) | 尝试获取非公平锁 |
重点说一下nonfairTryAcquire(int var1)和tryRelease(int var1)。
1.nonfairTryAcquire(int var1)
final boolean nonfairTryAcquire(int var1) {
//获取当前线程
Thread var2 = Thread.currentThread();
//获取当前锁的状态
int var3 = this.getState();
//如果锁的状态为0,则表明锁是完全释放的状态,可以被获取
if (var3 == 0) {
//将状态改为var1
if (this.compareAndSetState(0, var1)) {
//设置排他线程持有
this.setExclusiveOwnerThread(var2);
//返回true
return true;
}
//如果当前锁的状态不为0,则判断当前线程是否持有该锁,如果持有,该锁是可重入的,增//加锁的持有状态
} else if (var2 == this.getExclusiveOwnerThread()) {
int var4 = var3 + var1;
if (var4 < 0) {
throw new Error("Maximum lock count exceeded");
}
this.setState(var4);
return true;
}
return false;
}
2. tryRelease(int var1)
protected final boolean tryRelease(int var1) {
//将所状态减var1,存入局部变量var2
int var2 = this.getState() - var1;
//如果当前线程并不是该锁的持有者,则抛出异常
if (Thread.currentThread() != this.getExclusiveOwnerThread()) {
throw new IllegalMonitorStateException();
} else {
boolean var3 = false;
//否则判断该锁是否完全释放,如果完全释放,则清空排他线程持有
if (var2 == 0) {
var3 = true;
this.setExclusiveOwnerThread((Thread)null);
}
//重置锁状态
this.setState(var2);
return var3;
}
}
Sync基本实现了一个锁该有的功能,但是如果想同时支持共享锁和排他锁,它所提供的方法显然是不足的,接下来我们看看非公平锁NonfairSync和公平锁FairSync。
NonfairSync
NonfairSync继承了Sync,在Sync的基础上lock()和tryAcquire(int var1)。tryAcquire(int var1)直接调用Sync提供的nonfairTryAcquire(var1)非公平锁获取锁,lock方法则调用了AbstractQueuedSynchronizer的acquire(int arg),对不AbstractQueuedSynchronizer了解的同学可以直接点击AbstractQueuedSynchronizer进入查看。
lock()
final void lock() {
//如果当前锁的状态为0,且可以通过cas改为1,则将当前锁的持有者改为当前线程,否则调用//acquire(1),尝试从队列中获取锁
if (this.compareAndSetState(0, 1)) {
this.setExclusiveOwnerThread(Thread.currentThread());
} else {
this.acquire(1);
}
}
FairSync
FairSync也继承了Sync,并在Sync的基础上lock()和tryAcquire(int var1)。来看源码:
1.tryAcquire(int var1)
protected final boolean tryAcquire(int var1) {
//获取当前线程
Thread var2 = Thread.currentThread();
//获取当前锁状态
int var3 = this.getState();
//如果当前锁完全释放
if (var3 == 0) {
//则判断该同步器的队列中,该线程前是否还有节点在排队
//如果没有,则将同步器状态改为var1,将改锁提供给当前线程,并返回true
if (!this.hasQueuedPredecessors() && this.compareAndSetState(0, var1)) {
this.setExclusiveOwnerThread(var2);
return true;
}
//如果当前所没有完全释放,同时当前线程已持有该锁,则更新锁的状态,并提供锁,否则返 //回false
} else if (var2 == this.getExclusiveOwnerThread()) {
int var4 = var3 + var1;
if (var4 < 0) {
throw new Error("Maximum lock count exceeded");
}
this.setState(var4);
return true;
}
return false;
}
}
2.lock()
final void lock() {
//直接调用acquire(1),进入队列
this.acquire(1);
}
相信看完源码各位大神已经懂得差不多了,如果还觉得有的绕,笔者来提供两个锁的获取流程图对比一下便清楚了。
从流程图中很容易对比两种锁的获取锁机制有哪些不同,公平锁在每次获取时都会保证排在前面的线程优先获取到锁。看完Sync的构成,再来看ReentrantLock的API就很比较容易了。
方法 | 描述 |
lock() | 获取锁,依赖于NonfairSync和FairSync |
lockInterruptibly() | 可打断式的获取锁,依赖于AbstractQueuedSynchronizer |
tryLock() | 尝试以非公平锁获取一次锁,获取到则返回true,获取不到则返回false,不进入队列。依赖于Sync |
tryLock(long var1, TimeUnit var3) | 尝试在一定时间内获取锁,依赖于NonfairSync和FairSync |
unlock() | 释放一次锁,依赖于Sync |
newCondition() | 绑定一个ConditionObject,依赖于AbstractQueuedSynchronizer |
getHoldCount() | 获取持有该锁的线程数,依赖于Sync |
isHeldByCurrentThread() | 该锁是否被当前线程持有,依赖于Sync |
isLocked() | 判断锁是否处于持有状态,依赖于Sync |