【Beautiful JUC Part.10】AQS 并发灵魂人物
一、为什么需要AQS?
二、Semaphore和AQS的关系
CountDownLatch和ReentrantLock也是一样的。
三、AQS的认识
1、AQS的比喻
比喻:群面、单面
安排就坐、叫号、先来后到等HR的工作就是AQS做的工作
面试官不会去关心两个面试者是不是号码相同冲突了,也不想去管面试者需要一个地方坐着休息,这些都交给HR去做
比如:
Semaphore:一个人面试完了以后,后一个人才能进来继续面试
CountDownLatch:群面,等待10人到齐
Semaphore、CountDownLatch这些同步工具类,要做的就只是写下自己的“要人”规则。比如是“出一个,进一个”,或者说“凑齐10人,一起面试”。
剩下的招呼面试者的在那个活累活交给AQS来做
2、如果没有AQS
就需要每个协作工具自己实现:
- 同步状态的原子性管理
- 线程的阻塞与解除阻塞
- 队列的管理
在并发的场景下,自己正确且高效实现这些内容,是相当有难度的,所以我们用AQS来帮我们把这些脏活累活都搞定,我们只关注业务逻辑就够了。
3、AQS的作用
AQS是一个用于构建锁、同步器、协作工具类的工具类(框架)。有了AQS以后,更多的协作工具类都可以很方便得被写出来
一句话总结:有了AQS,构建线程协作类就容易多了。
4、AQS的重要性、地位
AbstractQueuedSynchronizer是Doug Lea写的,从JDK1.5加入的一个基于FIFO等待队列实现的一个用于实现同步器的基础框架,我们用IDE看AQS的实现类,可以发现实现类有以下这些。
四、AQS深入分析
1、AQS内部组成分析
AQS最核心的三大部分:
- state:状态
- 控制线程抢锁和配合的FIFO队列
- 期望协作工具类去实现的获取/释放等重要方法
①state状态
在ReentrantLock中:
-
state用来表示“锁”的占有情况,包括可重入计数
-
当state的值为0的时候,标识改Lock不被任何线程所占有
②控制线程抢锁和配合的FIFO队列
这个队列用来存放“等待的线程”,AQS就是“排队管理器”,当多个线程争用同一把锁时,必须有排队机制将那些没能拿到锁的线程串在一起,当锁释放时,锁管理器就会挑选一个合适的线程来占有这个刚刚释放的锁。
AQS会维护一个等待的线程队列,把线程都放到这个队列里,这是一个双向链表的形式
head是已经拿到锁的线程
③期望协作工具类去实现的方法
这里的获取和释放方法,是利用AQS的协作工具类里最重要的方法,是由协作类自己去实现的,并且含义各不相同。
获取方法
依赖state变量,经常会阻塞(比如获取不到锁的时候)
在Semaphore中,获取就是acquire方法,作用是获取一个许可证
而在CountDownLatch里面,获取就是await方法,作用是“等待,直到倒数结束”
释放方法
释放操作不会阻塞
在Semaphore中,释放就是release方法,作用是释放一个许可证
CountDownLatch里面,获取就是countDown方法,作用是“倒数1个数”
需要重写tryAcquire和tryRelease等方法
比如CountDownLatch
2、AQS应用实例、源码解析
①AQS用法
②CountDownLatch源码分析
内部类Sync继承AQS
构造方法
getCount()方法
await()方法
进行等待,直到倒数结束
调用的是AQS的方法
重要的是下面的if,如果tryAcquireShared(aag) < 0那么就入队。如果不小于0,就不需要等待了。
在CountDownLatch里面,实现了tryAcquireShared这个方法
如果剩余倒数的为0了,说明可以放行了,返回1,直接放行。如果state不等于0,返回-1,就会调用doAcquireSharedInterruptibly()方法让线程阻塞,把线程放到阻塞队列中。
countDown()方法
releaseSharea()是AQS实现的
这里调用的tryReleaseSharead()方法,是CountDownLatch自己实现的
用for循环做CAS的自旋操作,如果为0了,就不用释放。一旦这个方法返回true就会调用doReleaseShared()方法
总结
③Semaphore中的AQS
五、利用AQS实现一个自己的Latch门闩
一次释放,所有人都出发,一次性门闩。
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
/**
* 描述:自己用AQS实现一个简单的线程协作器
*/
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();
}
}
}