系列文章目录
Java多线程系列
第一章 Java线程池源码详解
第二章 可重入锁——ReentrantLock实现原理
目录
一、LOCK 接口定义
Lock接口是提供加锁操作
方法 | 作用 |
---|---|
lock | 阻塞直到获得锁 |
lockInterruptibly | 和lock类似,但可响应中断,抛中断异常 |
tryLock | 尝试获得锁,不管是否获取到,立马返回。返回true获得锁,返回false没获得。调用tryLock只会以非公平方式去争抢锁 |
tryLock(long time, TimeUnit unit) | 与tryLock类似,获取不到不是立马返回,会等待一段时间 |
unlock | 解锁 |
newCondition | 相当于syschronized的同步代码块的wait、notify、notifyAll,相比syschronized,ReentrantLock支持多个Condition |
二、什么是“可重入”
可重入就是,持有锁的线程可以多次重复加锁。锁不支持可重入很容易造成死锁,线程获得了锁,重入时产生死锁——自己等待自己释放锁。
2.1 ReentrantLock如何实现可重入
- 线程调用lock时:锁状态为0表示可加锁,使用CAS将状态设置成1进行锁的争抢。锁重入时,状态自增+1
- unlock时,状态自减-1,减到0时,锁释放,唤醒阻塞队列的线程,进行CAS锁争抢
final boolean tryLock() {
Thread current = Thread.currentThread();
// 状态为0表示可获得锁
int c = getState();
if (c == 0) {
// CAS操作,锁争抢,只有一个线程能成功加锁
if (compareAndSetState(0, 1)) {
// 记录当前持有锁的线程
setExclusiveOwnerThread(current);
return true;
}
} else if (getExclusiveOwnerThread() == current) {
// 锁重入啦:持有锁的线程 == 当前线程
// 状态自增
if (++c < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(c);
return true;
}
return false;
}
所释放:
protected final boolean tryRelease(int releases) {
// 这里releases == 1
// 状态自减1
int c = getState() - releases;
if (getExclusiveOwnerThread() != Thread.currentThread())
throw new IllegalMonitorStateException();
// 减到状态为0,说明锁释放了
boolean free = (c == 0);
if (free)
setExclusiveOwnerThread(null);
setState(c);
// 如果返回true,会唤醒阻塞队列的线程
return free;
}
三、公平锁与非公平锁
锁 | 描述 | 优缺点 |
---|---|---|
公平锁 | 所谓公平,就是排队的时候讲究先来后到,先来的先获取锁。 | 优点:不会“线程饥饿” 缺点:系统线程上下文切换次数高很多,从而吞吐量较低 |
非公平锁 | 不要求先来先获取锁,阻塞队列存在等待锁,新来的线程也可以立马获取到锁。很大程度减少线程上下文切换 | 优点:吞吐量高 缺点:可能存在“线程饥饿” |
线程饥饿:线程A在阻塞队列中等待锁,但是锁一直被后面来的线程争抢到锁,导致线程A一直拿不到锁,这是我们说线程A“饥饿”
根据网友测试,并发度较高时,非公平锁的吞吐量比公平锁高100倍
默认是非公平锁
public ReentrantLock() {
sync = new NonfairSync();
}
四 公平锁、非公平锁的实现
4.1 总体类图
公平同步器(FairSync)和 非公平器(NonfairSync)都继承自同步器类(Sync),Sync继承自抽象队列同步器类(AbstractQueuedSynchronizer, AQS)
UML:
+ Public
- Private
# Protected
~ Package
4.2 Sync类
Sync抽象类继承自AQS类,子类有FairSync和NonfairSync
主要把子类相同的tryRelease、tryLock、lock模板方法进行了实现
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L;
/**
* 进行非公平方式 tryLock.
*/
@ReservedStackAccess
final boolean tryLock() {
// 以非公平方式进行tryLock
}
/**
* 调用lock方法会先执行该方法,我理解主要是抽一层用于处理锁的重入
* 如果获取到锁返回true
*/
abstract boolean initialTryLock();
@ReservedStackAccess
final void lock() {
// 首先调用initialTryLock,如果加锁失败
if (!initialTryLock())
// 内部调用tryAcquire尝试获得锁,失败则会将线程加入阻塞队列(自旋一定次数后)
acquire(1);
}
@ReservedStackAccess
protected final boolean tryRelease(int releases) {
// 上文讲过这个方法
}
// 省略其他方法
}
4.3 NonfairSync类
非公平锁实现,使用CAS去争抢
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
/**
* 调用lock方法首先会执行initialTryLock
*/
final boolean initialTryLock() {
Thread current = Thread.currentThread();
// 直接CAS,体现了锁争抢是非公平的,谁CAS成功,谁加锁成功
if (compareAndSetState(0, 1)) { // first attempt is unguarded
setExclusiveOwnerThread(current);
return true;
} else if (getExclusiveOwnerThread() == current) {
// 处理可重入,上文介绍过
int c = getState() + 1;
if (c < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(c);
return true;
} else
return false;
}
/**
* tryAcquire是AQS定义模板方法,尝试获得独占锁
*/
protected final boolean tryAcquire(int acquires) {
// 这里也是CAS去设置state,体现了非公平
if (getState() == 0 && compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
}
4.4 FairSync类
公平锁,判断阻塞队列为空,再去争抢锁。
多加了一行代码 !hasQueuedPredecessors(),其他实现和NonfairSync一致。
hasQueuedThreads是AQS提供的方法:查询是否有线程正在等待获取锁
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
final boolean initialTryLock() {
Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
// 判断阻塞队列为空再去争抢锁
if (!hasQueuedThreads() && compareAndSetState(0, 1)) {
setExclusiveOwnerThread(current);
return true;
}
} else if (getExclusiveOwnerThread() == current) {
if (++c < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(c);
return true;
}
return false;
}
protected final boolean tryAcquire(int acquires) {
// 判断阻塞队列为空再去争抢锁
if (getState() == 0 && !hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
}