本次内容主要介绍AQS、AQS的设计及使用、ReentrantLock、ReentrantReadWriteLock以及手写一个可重入独占锁 1、什么是AQS?
AQS,队列同步器AbstractQueuedSynchronizer的简写,JDK1.5引入的,是用来构建锁或者其他同步组件的基础框架,它使用了一个int成员变量表示同步状态,通过内置的FIFO队列来完成资源获取线程的排队工作。AQS的作者Doug Lea大师期望它能够成为实现大部分同步需求的基础。
2、AQS的设计及其作用AbstractQueuedSynchronizer是一个抽象类,先看一下其类图。
AQS中里有一个volatile修饰int型的state来代表同步状态,使用同步器提供的3个方法(getState()、setState(int newState)和compareAndSetState(int expect,int update))来改变状态,因为它们能够保证状态的改变是安全的。
AQS使用的是模板方法模式,主要使用方式是继承,且通常将子类推荐定义为静态内部类,子类通过继承AQS并实现它的抽象方法来管理同步状态。AQS自身没有实现任何同步接口,它仅仅是定义了若干同步状态获取和释放的方法来供自定义同步组件使用,同步器既可以支持独占式地获取同步状态,也可以支持共享式地获取同步状态,这样就可以方便实现不同类型的同步组件(ReentrantLock、ReentrantReadWriteLock和CountDownLatch等)。AQS是实现锁(也可以是任意同步组件)的关键,在锁的实现中聚合同步器。可以这样理解二者之间的关系:
锁是面向使用者的,它定义了使用者与锁交互的接口(比如可以允许两个线程并行访问),隐藏了实现细节
同步器面向的是锁的实现者,它简化了锁的实现方式,屏蔽了同步状态管理、线程的排队、等待与唤醒等底层操作。锁和同步器很好地隔离了使用者和实现者所需关注的领域。实现者需要继承同步器并重写指定的方法,随后将同步器组合在自定义同步组件的实现中,并调用同步器提供的模板方法,而这些模板方法将会调用使用者重写的方法。
实现自定义同步组件时,将会调用AQS提供的模板方法,AQS的模板方法如下:
AQS提供的模板方法基本上分为3类:独占式获取与释放同步状态、共享式获取与释放同步状态和查询同步队列中的等待线程情况。AQS中可重写的方法如下:
AQS中有一个内部类Node,用于构造一个队列来保存排队等待获取锁的线程。看一下Node的源码及其简单说明:
static final class Node { /**标记线程是因为获取共享资源失败被阻塞添加到队列中的*/ static final Node SHARED = new Node(); /**表示线程因为获取独占资源失败被阻塞添加到队列中的*/ static final Node EXCLUSIVE = null; /**表示线程因为中断或者等待超时,需要从等待队列中取消等待*/ static final int CANCELLED = 1; /**表示当前线程占有锁,队列中没有存放线程引用头结点的后继结点A处于等待状态, * 如果已占有锁的线程释放锁或被CANCEL之后就会通知结点A去获取锁。*/ static final int SIGNAL = -1; /**当持有锁的线程调用了Condition(下面会讲到具体运用)的signal()方法之后,处于同一condition下的等待线程会去竞争锁*/ static final int CONDITION = -2; /**表示把waitStatus的值,指示下一个acquireShared应该无条件传播*/ static final int PROPAGATE = -3; /**表示当前线程的等待状态*/ volatile int waitStatus; volatile Node prev; volatile Node next; /**表示进入AQS队列中的线程引用*/ volatile Thread thread; Node nextWaiter; final boolean isShared() { return nextWaiter == SHARED; } final Node predecessor() throws NullPointerException { Node p = prev; if (p == null) throw new NullPointerException(); else return p; } Node() { // Used to establish initial head or SHARED marker } Node(Thread thread, Node mode) { // Used by addWaiter this.nextWaiter = mode; this.thread = thread; } Node(Thread thread, int waitStatus) { // Used by Condition this.waitStatus = waitStatus; this.thread = thread; } }