1、AQS是什么
有协作功能的一些类,如ReentrantLock和Semaphore,CountDownLatch、ReentrantReadWriteLock,它们底层都实现了一个基类,这就是AQS
AQS可以理解为是一个工具类,帮助我们实现线程之间的协作
2、AQS演示
public class Semaphore implements java.io.Serializable { private static final long serialVersionUID = -3222578661600680210L; private final Sync sync; abstract static class Sync extends AbstractQueuedSynchronizer { private static final long serialVersionUID = 1192457210091910933L; Sync(int permits) { setState(permits); } final int getPermits() { return getState(); } final int nonfairTryAcquireShared(int acquires) { for (;;) { int available = getState(); int remaining = available - acquires; if (remaining < 0 || compareAndSetState(available, remaining)) return remaining; } } }
都是一个Sync内部类,去继承我们的AbstractQueuedSynchronizer(AQS)类,我们其他的类也是这个套路
3、AQS原理
AQS由三个核心部分组成:
- State
- 控制线程强锁和配合的FIFO队列
- 期望协作工具类去实现的获取/释放等重要的方法
3.1、State
state的具体含义,会根据具体实现类的不同而不同,比如在Semaphore中,他表示“剩余的许可证的数量”,而在CountDownLatch中,它表示“还剩余倒数的数”
state是volatile修饰的,会被并发地修改,所以素有修改state的方法都需要保证线程安全,比如getState,setState已经compareAndSetState操作来读取和更新这个状态,这些方法都是依赖于atomic包的支持
//通过unsafe顶层指令来保证原子性 protected final boolean compareAndSetState(int expect, int update) { // See below for intrinsics setup to support this return unsafe.compareAndSwapInt(this, stateOffset, expect, update); }
在ReentrantLock中,state是用来表示锁的占有情况,包括可重入计数,当state的值为0的时候,标识该Lock不被任何线程锁占有
3.2、FIFO队列
这个队列是用来存放“等待的线程”,AQS就是“排队管理员”,当多个线程竞争锁的时候,只有一个线程可以获取,其他的线程串在一起进行等待。当锁释放时,锁管理器就会挑选一个合适的线程来占有刚刚释放的锁
ASQ会维护一个等待的线程队列,把线程都放到这个队列中,且这个队列是双向链表
3.3、获取/释放的方法
这个需要实现类自己实现,会依赖state的值,经常会阻塞(获取不到锁的时候)
3.4、AQS的实现类
- 写一个类,想好协作的逻辑,实现获取/释放的方法
- 内部写一个Sync类继承AbstractQueuedSynchronizer
- 根据是否独占重写tryAcquirte/tryRelease或tryAcquireShared(int acquires)和tryReleaseShared(int releases)等方法,在之前写的获取/释放方法中调用AQS的acquire/release或者Shared方法
4、CountDownLatch源码分析
构造方法
//构造方法 public CountDownLatch(int count) { if (count < 0) throw new IllegalArgumentException("count < 0"); this.sync = new Sync(count); } //调用sync的构造 Sync(int count) { etState(count); } //设置state值 protected final void setState(int newState) { state = newState; }
getCount方法
//调用getCount方法 public long getCount() { return sync.getCount(); } int getCount() { return getState(); } //最终返回的是state 的值 protected final int getState() { return state; }
countDown方法
public void countDown() { sync.releaseShared(1); } public final boolean releaseShared(int arg) { if (tryReleaseShared(arg)) { //唤醒队列中的所有线程 doReleaseShared(); return true; } return false; } protected boolean tryReleaseShared(int releases) { //死循环 for (;;) { //获取到state的值 int c = getState(); if (c == 0) return false; //如果不为0,说明还可以减 int nextc = c-1; //用原子操作保证线程安全 if (compareAndSetState(c, nextc)) return nextc == 0; } }
await方法
public void await() throws InterruptedException { sync.acquireSharedInterruptibly(1); } public final void acquireSharedInterruptibly(int arg) throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException(); //判断state是不是0 if (tryAcquireShared(arg) < 0) //进入到阻塞队列中 doAcquireSharedInterruptibly(arg); } protected int tryAcquireShared(int acquires) { return (getState() == 0) ? 1 : -1; }
- 调用CountDownLatch的await方法时,便会尝试获取“共享锁”,不过一开始是获取不到该锁的,于是线程被阻塞
- 而“共享锁”可获取到的条件,就是“锁计数器”的值为0
- 而“锁计数器”的初始值为count,每当一个线程调用该CountDownLatch对象的countDown()方法时,才将“锁计数器”-1
- count个线程调用countDown()之后,“锁计数器”才为0,而前面提到的等待获取线程的共享锁的线程才能继续运行
5、Semaphore源码分析
获取许可证
public void acquire(int permits) throws InterruptedException { //判断需要获取的许可证个数不能小于0,否则抛出异常 if (permits < 0) throw new IllegalArgumentException(); sync.acquireSharedInterruptibly(permits); } public final void acquireSharedInterruptibly(int arg) throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException(); if (tryAcquireShared(arg) < 0) //小于0,就去等待 doAcquireSharedInterruptibly(arg); } protected int tryAcquireShared(int acquires) { return nonfairTryAcquireShared(acquires); } //返回值如何小于0,获取许可证失败 final int nonfairTryAcquireShared(int acquires) { //死循环自旋 for (;;) { //getState()获取许可证数量 int available = getState(); //判断当前许可证-获取许可证的值 int remaining = available - acquires; if (remaining < 0 || //如果获取的值够,就会进行cas操作保证线程安全 compareAndSetState(available, remaining)) return remaining; } }
- 在Semaphore中,state表示许可证的剩余数量
- 看tryAcquire方法,判断nonfairTryAcquireShared大于等于0的话,代表获取成功
- 这里会先检查剩余的许可证数量够不够这次需要的,用减法来计算,如果不够,就返回负数,如果够了,就用cas操作自旋来改变state的状态,知道改变成功返回整数;期间如果被其他人修改了导致获取为负数,也会直接返回负数代表失败
释放许可证
public void release(int permits) { //判断释放的许可证,小于0抛出异常 if (permits < 0) throw new IllegalArgumentException(); sync.releaseShared(permits); } public void release(int permits) { if (permits < 0) throw new IllegalArgumentException(); sync.releaseShared(permits); } protected final boolean tryReleaseShared(int releases) { //自旋 for (;;) { int current = getState(); int next = current + releases; //判断当前的state+需要释放的许可证不能大于最大允许数,大于就抛出异常 if (next < current) // overflow throw new Error("Maximum permit count exceeded"); //使用cas操作,对state进行设置 if (compareAndSetState(current, next)) return true; } }
6、ReentrantLock源码分析
解锁
public void unlock() { sync.release(1); } public final boolean release(int arg) { //tryRelease()代表锁是否被释放了 if (tryRelease(arg)) { Node h = head; if (h != null && h.waitStatus != 0) //唤醒后面的节点 unparkSuccessor(h); return true; } return false; } protected final boolean tryRelease(int releases) { //getState()获取当前的次数,这个就是当前的次数减一 int c = getState() - releases; //判断当前线程是不是持有锁的线程,不是就抛出异常 if (Thread.currentThread() != getExclusiveOwnerThread()) throw new IllegalMonitorStateException(); boolean free = false; //当前次数减完一后,如果为0,就释放锁 if (c == 0) { //free代表锁是不是自由的 free = true; setExclusiveOwnerThread(null); } //不为0,则代表是重入过,只是state-1操作 setState(c); return free; }
- 由于可重入的,所以state代表重入的次数,每次释放锁,先判断是不是当前持有锁的线程释放的,如果不是抛出异常;如果是的,重入的次数减一,如果减到0,就说明完全释放了,于是free就是true,并且将state设置为0
加锁
final void lock() { if (compareAndSetState(0, 1)) //将当前的线程设置为持有锁的线程 setExclusiveOwnerThread(Thread.currentThread()); else // acquire(1); } public final void acquire(int arg) { if (!tryAcquire(arg) && //addWaiter()添加到队列中 acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); } protected final boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); //等于0代表没有线程持有 if (c == 0) { if (!hasQueuedPredecessors() && //通过cas操作来获取到这把锁 compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } //我这个线程正好是持有这把锁,是一个重入的操作 else if (current == getExclusiveOwnerThread()) { //重入的次数+1 int nextc = c + acquires; //判断重入后的值是不是溢出 if (nextc < 0) throw new Error("Maximum lock count exceeded"); //将state值设置进去 setState(nextc); return true; } //代表锁被其他线程持有,直接返回false return false; }
7、尝试利用AQS实现自己的Latch门闩
一次性门闩
public class OneShotLatch { private final Sync sync = new Sync(); //获取 public void await(){ sync.acquireShared(0); } //释放 public void signal(){ sync.releaseShared(0); } //不需要强制重写,是因为不确定我们需要重写哪些方法 static class Sync extends AbstractQueuedSynchronizer { @Override protected int tryAcquireShared(int arg) { return (getState() == 1 ) ? 1 : -1; } @Override protected boolean tryReleaseShared(int arg) { setState(1); return true; } } public static void main(String[] args) { final OneShotLatch oneShotLatch = new OneShotLatch(); for (int i = 0; i < 10; i++) { new Thread(new Runnable() { public void run() { System.out.println(Thread.currentThread().getName() + "尝试获取门闩,获取失败那就等待"); oneShotLatch.await(); System.out.println( "开闸放行" +Thread.currentThread().getName() + "继续运行"); } }).start(); } try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } oneShotLatch.signal(); new Thread(new Runnable() { public void run() { System.out.println(Thread.currentThread().getName()+"尝试获取latch,获取失败那就等待"); oneShotLatch.await(); System.out.println("开闸放行"+Thread.currentThread().getName()+"继续运行"); } }).start(); } }