Java中的显式锁
Lock接口
在java5之前,要实现同步只能用synchronize,在java5后,随着并发工具包的出现,出现了另一种同步方式--显式锁,显式锁提供了更丰富,粒度更细的加锁方式,其中的"锁王"就是--Lock接口.
先看看它的方法列表:
方法 | 介绍 |
---|---|
lock() |
获取锁,如果没有锁可用,就一直阻塞 |
lockInterruptibly() |
获取锁,直到当前线程被中断 |
newCondition() |
返回绑定到此锁的Condition实例(用于线程通信,使用方法详见线程通信及其工具类) |
tryLock() |
获取锁,若获取到立刻返回true,否则立刻返回false |
tryLock(long time, TimeUnit unit) |
在指定时间内获取锁,超时返回false |
unlock() |
释放锁 |
Lock使用方式一般如下:
Lock l = ...;
l.lock();
try {
// access the resource protected by this lock
} finally {
l.unlock();//注意unlock要放在finally块内,否则出现异常锁得不到释放
}
Lock与synchronize的区别
既然有了synchronize,为什么还要Lock?我们来看看Lock与synchronize的区别:
- 底层实现不同:
- synchronize是jvm支持的关键字,实现原理是同步监视器(详见...)
- Lock的底层实现依赖AQS(详见浅析AQS)
- 使用方法不同
- synchronize不用手动释放锁
- Lock需要手动释放锁
- Lock比synchronize提供更丰富的加锁方法以及更细的粒度控制
- Lock可调用
lockInterruptibly
实现中断等待获取锁 - Lock可调用
tryLock
或tryLock
(long time, TimeUnit unit)
实现获取锁等待时间的控制 - Lock的实现类大部分提供了公平锁和非公平锁的实现,而synchronize是非公平的
- Lock可实现多个条件绑定,实现精确唤醒,而synchronize只能随机唤醒
线程八锁--显式锁的实现
先看看Lock的继承体系
Lock继承体系
表面上看,Lock只有可重入锁,读写锁的实现类,实际上可重入锁和读写锁都有其内部类实现公平锁和非公平锁
可重入锁与非重入锁
可重入锁又被称为递归锁.在jdk中,可重入锁显式锁的实现类是ReentrantLock
可重入锁类图.pngReentrantLock实现了Lock接口,且有三个内部类分别是ReentrantLock.FairSync,ReentrantLock.NonfairSync和ReentrantLock.Sync
ReentrantLock.FairSync,ReentrantLock.NonfairSync分别是ReentrantLock的公平锁实现和非公平锁实现
可重入的意思是,同一个线程可以多次获取同一个锁,举个例子
ReentrantLock lock = new ReentrantLock();
lock.lock();
try {
System.out.println("第一次获取锁");
//这里重复获取了同一把锁
lock.lock();
try {
System.out.println("第二次获取锁");
} finally {
lock.unlock();
}
} finally {
lock.unlock();
}
若上述代码不是可重入锁,则会在第二次获取锁时,发生死锁,因为在等待第一次获取的锁释放,而第一次获取的锁要在第二次获取锁后才释放
jdk中所有的锁都是可重入锁,包括synchronized
那么我们再来看看,可重入锁是怎么实现的
public void lock() {
sync.lock();
}
可以看到是调用了内部类ReentrantLock.Sync的lock(),而ReentrantLock.Sync又有公平锁和非公平锁实现,我们看看公平锁实现
final void lock() {
acquire(1);
}
这个acquire()正是AQS的方法,不了解AQS的同学可以看看浅析AQS,继续往里深挖
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}
可以看到在acquire方法里首先执行tryAcquire方法,这个方法需要子类覆盖否则直接抛异常,所以我们要看的是ReentrantLock.FairSync里的tryAcquire方法
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
//调用AQS的getState方法获取线程状态
int c = getState();
//若线程状态为0,表示当前线程还没有获取锁
if (c == 0) {
if (/** 此方法是查看有没有其他线程排在自己签名 **/
!hasQueuedPredecessors()
&&
/**CAS设置state为1,回溯前面代码,传的值是1**/
compareAndSetState(0, acquires)) {
//设置当前线程拥有独占访问权
setExclusiveOwnerThread(current);
return true;
}
}
//若当前线程已拥有独占访问权
else if (current == getExclusiveOwnerThread()) {
//使state++,这里就是重入锁的关键
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
上面这段代码大概意思就是:当前线程如果还未获取锁,那就尝试获取(查看有无线程排在自己前面),若果已获取了锁,就让state++.所以state=0时,线程无锁,state>0时,state的值就表示该线程获取重入锁的次数.同理,若释放重入锁,state--
//ReentrantLock类
public void unlock() {
sync.release(1);
}
//AQS类
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);//唤醒后继线程
return true;
}
return false;
}
//ReentrantLock.Sync类
protected final boolean tryRelease(int releases) {
int c = getState() - releases;//重入计数减1
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {//若state为0,则释放锁
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
公平锁与非公平锁
jdk里的公平锁与非公平锁不是一个具体类,而是一种锁的公平实现与非公平实现,回顾一下可重入锁的类图
可重入锁类图.pngReentrantLock.FairSync,ReentrantLock.NonfairSync就是ReentrantLock的公平实现和非公平实现
公平锁与非公平锁之间的区别就是在获取锁的时候,非公平锁会先尝试"插队","插队"失败就和公平锁一样排队等待,但在每次有机会获取锁时,非公平锁都会尝试"插队"
//公平锁实现
final void lock() {
acquire(1);
}
//非公平锁实现
final void lock() {
if (compareAndSetState(0, 1)) //插队
setExclusiveOwnerThread(Thread.currentThread());//若插队成功,则赋予当前线程访问权
else
acquire(1);
}
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
//公平版tryAcquire
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;
}
//非公平版tryAcquire
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
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;
}
return false;
}
参考文献