AQS

AQS(AbstractQueuedSynchronizer)

  • AQS是一个用于构建锁、同步器、协作工具类的工具类(框架)。有了AQS以后,更多的协作工具类都可以很方便得被写出来
  • ReentrantLock,Semaphore,ReentrantLock和Semaphore,包括CountDownLatch、ReentrantReadWriteLock都有这样类似的“协作”(或者叫“同步”)功能,其实,它们底层都用了一个共同的基类这就是AQS
  • 有了AQS,构建线程协作类就容易多了。
  • Semaphore内部有一个Sync类,Sync类继承了AQS

  • CountDownLatch也一样

AQS核心三部分

  • state
  • 这里的state的具体含义,会根据具体实现类的不同而不同比如在Semaphore里,它表示“剩余的许可证的数量”,而在CountDownLatch里,它表示“还需要倒数的数量”
  • ReentrantLock中,state表示”锁”的占有情况,包括可重入计数.值为0的时候,标识改Lock不被任何线程所占有.
  • state是volatile修饰的,会被并发地修改,所以所有修改state的方法都需要保证线程安全,比如getState、setState以及compareAndSetState操作来读取和更新这个状态。这些方法都依赖于j.u.C.atomic包的支持
  • 控制线程抢锁和配合的FIFO队列
  • 这个队列用来存放“等待的线程”,AQS就是“排队管理器”当多个线程争用同一把锁时,必须有排队机制将那些没能拿到锁的线程串在一起。当锁释放时,锁管理器就会挑选-个合适的线程来占有这个刚刚释放的锁
  • AQS会维护一个等待的线程队列,把线程都放到这个队列里
  • 这是一个双向形式的队列

  • 期望协作工具类去实现的获取/释放等重要方法
  • 这里的获取和释放方法,是利用AQS的协作工具类里最重要的方法,是由协作类自己去实现的,并且含义各不相同
  • 获取方法
  • 获取操作会依赖state变量,经常会阻塞(比如获取不到锁的时候)
  • 在Semaphore中,获取就是acquire方法,作用是获取·一个许可证
  • 而在CountDownLatch里面,获取就是await方法,作用是“等待,直到倒数结束”
  • 释放方法
  • 释放操作不会阻塞
  • 在Semaphore中,释放就是release方法,作用是释放一个许可证
  • CountDownLatch里面,获取就是countDown方法,作用是“倒数1个数”
  • 需要重写tryAcquire和tryRelease或tryAcquireShared (int acquires)和tryReleaseShared(intreleases)等方法

AQS在CountDownLatch

  • 调用CountDownLatch的await方法时,便会尝试获取“共享锁”,不过一开始是获取不到该锁的,于是线程被阻塞。
  • 而“共享锁”可获取到的条件,就是“锁计数器”的值为0。
  • 而“锁计数器”的初始值为count,每当一个线程调用该CountDownLatch对象的countDown()方法时,才将“锁计数器”-1
  • count个线程调用countDown()之后,“锁计数器”才为0,而前面提到的等待获取共享锁的线程才能继续运行。

AQS在Semaphore

  • 在Semaphore中,state表示许可证的剩余数量
  • 看tryAcquire方法,判断nonfairTryAcquireShared大于等于0的话,代表成功
  • 这里会先检查剩余许可证数量够不够这次需要的,用减法来计算,如果直接不够,那就返回负数,表示失败,如果够了,就用自旋加compareAndSetState来改变state状态,直到改变成功就返回正数;或者是期间如果被其他人修改了导致剩余数量不够了,那也返回负数代表获取失败

AQS在ReentrantLock

分析释放锁的方法tryRelease

由于是可重入的,所以state代表重入的次数,每次释放锁先判断是不是当前持有锁的线程释放的,如果不是就抛异常,如果是的话,重入次数就减一,如果减到了0,就说明完全释放了,于是free就是true,并且把state设置为0。

AQS用法

第一步:写一个类,想好协作的逻辑,实现获取/释放方法。

第二步:内部写一个Sync类继承AbstractQueuedSynchronizer

第三步:根据是否独占来重写tryAcquire/tryRelease或tryAcquireShared (int acquires)和tryReleaseShared(intreleases)等方法,在之前写的获取/释放方法中调用AQS的acquire/release或者Shared方法

public class OneShotLatch {

private final Sync sync = new Sync();

public void signal() {
sync.releaseShared(0);
}
public void await() {
sync.acquireShared(0);
}

private 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) throws InterruptedException {
OneShotLatch oneShotLatch = new OneShotLatch();
for (int i = 0; i < 10; i++) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"尝试获取latch,获取失败那就等待");
oneShotLatch.await();
System.out.println("开闸放行"+Thread.currentThread().getName()+"继续运行");
}
}).start();
}
Thread.sleep(5000);
oneShotLatch.signal();

new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"尝试获取latch,获取失败那就等待");
oneShotLatch.await();
System.out.println("开闸放行"+Thread.currentThread().getName()+"继续运行");
}
}).start();
}
}

posted @ 2024-03-01 15:42  wangzhilei  阅读(22)  评论(0编辑  收藏  举报