目录:
- 1、Lock介绍
- 2、Lock的实现类ReentrantLock
- 3、AQS简介
- 4、ReentrantLock类体系
- 5 、Lock使用
- 6、AQS原理:
- 6.1、AQS内存模型
- 6.2、AQS类体系
- 6.3、AQS模板方法
- 6.4、Node节点
- 6.5、CHL同步队列
- 6.6、同步状态获取(acquire)与释放(release)
- 6.7、线程阻塞与唤醒
- 7、AQS-产品之一ReentrantLock
- 7.1、图解:获取锁、释放锁、入同步队列
- 7.2、公平锁&非公平锁
- 8、面试常见问题
- 1、公平锁(FairSync)和非公平锁(NonfairSync)的区别?
- 2、ReentrantLock如何处理线程中断的?
- 3、未完待续~
1、Lock介绍
Lock 接口实现类提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。此实现允许更灵活的结构,可以具有差别很大的属性,可以支持多个相关的 Condition 对象(Condition实现类ConditonObject来实现线程的通知/与唤醒机制,关于Condition传送门
)。
2、 Lock的实现类ReentrantLock
ReentrantLock是根据AQS实现的独占锁(exclusive)。
3、 AQS简介
AQS全称AbstractQueuedSynchronizer,是java并发包中的一个类,该类更像是一个框架(模板方法模式),提供了一些模板方法供子类实现,从而实现了不同的同步器,如下图所示。ReentrantLock,ReentrantReadWriteLock,CountDownLatch、ThreadPoolExecutor等
这些常见类都使用了AQS。
4、 ReentrantLock类体系
5、Lock使用
eg:加锁/解锁伪代码
Lock lock = new xxxLock();
try{
// 加锁
lock.lock();
balabala作相应业务
} finally{
// 解锁
lock.unlock();
}
6、AQS原理:
6.1、AQS内存模型
6.2、AQS类体系
6.3、AQS模板方法
6.4、Node节点
6.5、CHL同步队列
6.6、同步状态获取(acquire)与释放(release)
6.7、线程阻塞与唤醒
7、AQS-产品之一ReentrantLock:
7.1、图解:获取锁、释放锁、入同步队列
背景:Thread-A 、Thread-B、Thread-C顺序获取锁,模拟内存中同步队列变化。还要一种特殊情况:Thread-D获取锁失败,同时入同步队列失败(最倒霉的线程D)。
分析下第7-3步骤为何要倒序循环唤醒节点?
7.2、公平锁&非公平锁
8、面试常见问题:
- 1、公平锁(FairSync)和非公平锁(NonfairSync)的区别?
锁的公平性:获取锁顺序而言的。
共同点:都是继承自ReentrantLock内部类Sync。
差异:- 公平锁,那么锁的获取顺序就应该符合请求的绝对时间顺序,也就是 FIFO。
- 非公平锁,在获取锁的时候,会先通过 CAS 进行抢占。
另外公平锁
判断条件多了hasQueuedPredecessors()
方法,也就是加入了[同步队列中当前节点是否有前驱节点]的判断,如果该方法返回 true,则表示有线程比当前线程更早地请求获取锁, 因此需要等待前驱线程获取并释放锁之后才能继续获取锁。
公平锁加锁方法:
// 公平锁-获取锁的方法
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;
}
非公平锁加锁方法:
final void lock() {
// 尝试CAS加锁
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
// 第一次尝试失败后,走父类通用加锁逻辑
acquire(1);
}
// 父类模板方法回调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;
}
- 2、ReentrantLock如何处理线程中断的?
阻塞队列使用的LockSupport.park();
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
因为LockSupport.park(),无法响应Thread.interrupt(); 所以当unpark()后使用Thread.interrupted()来判断线程是否有中断过。如果中断过整个唤醒的线程在外层方法会继续执行一次中断,详情源码如下:
static void selfInterrupt() {
Thread.currentThread().interrupt();
}