Lock底层原理—ReentrantLock、AQS、Condition
先来看看J.U.C包下的结构
- juc-locks 锁框架
- juc-atomic 原子类框架
- juc-sync 同步器框架
- juc-collections 集合框架
- juc-executors 执行器框架
而我们今天的主角ReentrantLock
,就是juc-locks包下的。上一篇刚算了解一下synchronized
的底层原理,所以就想看看ReentrantLock
和它的区别到底是什么,改进在哪里,适用于什么场景。
1. Lock
Lock
和ReadWriteLock
是两大锁的根接口,下面看一下JDK 1.8 API中如何描述的
![224200f3453df06f69dbde9cef72b00a.png](https://i-blog.csdnimg.cn/blog_migrate/d6b01874814e4d5ce47030db0d3ffd01.jpeg)
通过API的介绍,sychronized
和Lock
的区别可以分为如下:
Lock
增加了灵活性,最主要的就是支持多个Condition。Lock
提供了非阻塞获得锁的方式,synchronized
有自旋锁。Lock
更适合使用在复杂的同步,而synchronized
更简单、简杰。Lock
可以设置公平锁和非公平锁,synchronized
只有非公平锁。
浏览一下Lock
源码
package java.util.concurrent.locks;
import java.util.concurrent.TimeUnit;
public interface Lock {
//获得锁
void lock();
//获取锁定,除非当前线程中断
void lockInterruptibly() throws InterruptedException;
//只有在调用时才可以获得锁
boolean tryLock();
//如果在给定的等待时间内是空闲的,并且当前的线程尚未得到 interrupted,则获取该锁
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
//释放锁。
void unlock();
//返回一个新Condition绑定到该实例Lock实例
Condition newCondition();
}
2. ReentrantLock
Lock
了解之后,看一下实现类是如何实现接口的方法的。最常用的实现类就是ReentrantLock
。
2.1. 公平锁和非公平锁
public ReentrantLock() {
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
从构造函数上可以看出来ReentrantLock
实现了公平锁和非公平锁两种,默认一般使用非公平锁,它的效率和吞吐量都比公平锁高的多。
2.2. Sync
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L;
abstract void lock();
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;
}
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
protected final boolean isHeldExclusively() {
return getExclusiveOwnerThread() == Thread.currentThread();
}
final ConditionObject newCondition() {
return new ConditionObject();
}
final Thread getOwner() {
return getState() == 0 ? null : getExclusiveOwnerThread();
}
final int getHoldCount() {
return isHeldExclusively() ? getState() : 0;
}
final boolean isLocked() {
return getState() != 0;
}
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
setState(0); // reset to unlocked state
}
}
2.3. NonfairSync
非公平锁实现
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
final void lock() {
/**
* 先抢锁
*/
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
2.4. FairSync
说完了非公平锁,那就不得不提公平锁的实现了。
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
final void lock() {
acquire(1);
}
//非公平锁调用的是父类sync的nonfairTryAcquire方法
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;
}
}
有了非公平锁做铺垫,了解公平锁就方便多了。公平锁和非公平锁不同之处在于,公平锁在获取锁的时候,不会先去检查state状态。
公平锁和非公平锁最主要的区别在于lock()
,一个是先排队,一个是先抢锁。但是非公平锁的吞吐量更大。
通过查看ReentrantLock
中的底层实现,我们发现了AQS的踪迹,先看一下AQS是什么,然后分析一下整个锁的流程。
3. AbtractQueuedSynchronizer
AbtractQueuedSynchronizer
抽象的队列式的同步器,AQS定义了一套多线程访问共享资源的同步器框架,是JUC包下整体的基础框架。AQS是一个抽象的队列同步器,维护着一个同步队列和State状态 。底层使用了Unsafe类进行CAS操作。Sync
就是AbstractQueuedSynchronizer
的子类,AQS采用了模板方法模式,提高了扩展性有保证了规范性。
AQS只是一个框架,具体资源的获取/释放方式交由自定义同步器去实现
JDK 1.8 API中的介绍如下
![25d8e3092732ed50e4411f41d7d2c995.png](https://i-blog.csdnimg.cn/blog_migrate/6d3f2806db20ce4ccd86e8f09c6ab4fa.jpeg)
从上述介绍中可以看出:
- 同步器依赖于每个Node的状态,状态都是通过CAS来设置的。
- AQS支持独占模式和共享模式 。
还有一些关于Condition
,下面再去研究。
看一下其内部图片:
![b2e254d2a515d8003b5917d8cf5efecc.png](https://i-blog.csdnimg.cn/blog_migrate/fad92a20436a46490a35ca6e20435016.jpeg)
AbtractQueuedSynchronizer
内部维持着一个
虚拟的双向同步队列,因为队列中只有头节点和尾节点的指针。
Node
是AQS中的节点类
/** Marker to i