AQS是AbstractQueuedSynchronizer的简称。
AbstractQueuedSynchronizer 同步状态AbstractQueuedSynchronizer 内部有一个state属性,用于指示同步的状态:
private volatile int state;state的字段是个int型的,它的值在AbstractQueuedSynchronizer中是没有具体的定义的,只有子类继承AbstractQueuedSynchronizer那么state才有意义,如在ReentrantLock中,state=0表示资源未被锁住,而state>=1的时候,表示此资源已经被另外一个线程锁住。
AbstractQueuedSynchronizer中虽然没有具体获取、修改state的值,但是它为子类提供一些操作state的模板方法:
获取状态 protected final int getState() { return state; } 更新状态 protected final void setState(int newState) { state = newState; } CAS更新状态 protected final boolean compareAndSetState(int expect, int update) { return unsafe.compareAndSwapInt(this, stateOffset, expect, update); } AQS 等待队列AQS 等待列队是一个双向队列,队列中的成员都有一个prev和next成员,分别指向它前面的节点和后面的节点。
队列节点在AbstractQueuedSynchronizer内部,等待队列节点由内部静态类Node表示:
static final class Node { ... } 节点模式队列中的节点有两种模式:
独占节点:同一时刻只能有一个线程访问资源,如ReentrantLock
共享节点:同一时刻允许多个线程访问资源,如Semaphore
节点的状态等待队列中的节点有五种状态:
CANCELLED:此节点对应的线程,已经被取消
SIGNAL:此节点的下一个节点需要一个唤醒信号
CONDITION:当前节点正在条件等待
PROPAGATE:共享模式下会传播唤醒信号,就是说当一个线程使用共享模式访问资源时,如果成功访问到资源,就会继续唤醒等待队列中的线程。
自定义同步锁为了便于理解,使用AQS自己实现一个简单的同步锁,感受一下使用AQS实现同步锁是多么的轻松。
下面的代码自定了一个CustomLock类,继承了AbstractQueuedSynchronizer,并且还实现了Lock接口。
CustomLock类是一个简单的可重入锁,类中只需要重写AbstractQueuedSynchronizer中的tryAcquire与tryRelease方法,然后在修改少量的调用就可以实现一个最基本的同步锁。
CustomLock是实现了Lock接口,所以要重写lock和unlock方法,不过方法的代码很少只需要调用AQS中的acquire和release。
然后为了演示AQS的功能写了一个小演示程序,启动两根线程,分别命名为线程A和线程B,然后同时启动,调用runInLock方法,模拟两条线程同时访问资源的场景:
public class CustomLockSample { public static void main(String[] args) throws InterruptedException { Lock lock = new CustomLock(); new Thread(()->runInLock(lock), "线程A").start(); new Thread(()->runInLock(lock), "线程B").start(); } private static void runInLock(Lock lock){ try { lock.lock(); System.out.println("Hello: " + Thread.currentThread().getName()); Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); }finally { lock.unlock(); } } } 访问资源(acquire)在CustomLock的lock方法中,调用了 acquire(1),acquire的代码如下 :
public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); }CustomLock.tryAcquire(...):CustomLock.tryAcquire 判断当前线程是否能够访问同步资源
addWaiter(...):将当前线程添加到等待队列的队尾,当前节点为独占模型(Node.EXCLUSIVE)
acquireQueued(...):如果当前线程能够访问资源,那么就会放行,如果不能那当前线程就需要阻塞。
selfInterrupt:设置线程的中断标记