AQS抽象队列同步器详解及工具类拓展
信号量和栅栏和倒计数器
CountDownLatch
Java1.5被引入的一个工具类,常被称为:倒计数器。创建对象时,传入指定数值作为线程参与的数量;
await:方法等待计数器值变为0,在这之前,线程进入等待状态;
countdown:计数器数值减一,直到为0;
经常用于等待其他线程执行到某一节点,在继续执行当前线程代码
使用场景实例:
1、统计线程执行的情况
2、压力测试中,使用countDownLatch实现最大程度的并发处理
3、多个线程之间,相互通信,比如线程异步调用完接口,结果通知
demo
1 | package com.wyj.jvm.cdl.benchmark; |
CyclicBarrier
也是1.5加入的,又称为“线程栅栏”
创建对象时,执行栅栏线程数量。
await:等指定数量的线程都处于等待状态时,继续执行后续代码。
barrierAction:线程数量到了指定量之后,自动触发执行指定任务。
和CountDownLatch重要区别在于,CyclicBarrier对象可多次触发执行;
典型场景:
1、数据量比较大时,实现批量插入数据到数据库;
2、数据统计,30个线程统计30天数据,全部统计完毕后,执行汇总
demo
1 | package com.wyj.jvm.cdl.benchmark; |
Semaphore
又称”信号量“,控制多个线程争抢许可。
acquire:获取一个许可,如果没有就等待,
release:释放一个许可。
availablePermits:方法得到可用的许可数目
典型场景
1、代码并发处理限流
demo
1 | package com.wyj.jvm.semaphore; |
运行结果
1 | 楼上出来迎接贵宾一位,贵宾编号vip-000,... |
上面三个jdk类其实内部实现相似,都是将线程先挂起,达到某条件的时候,再运行。
自己手写一个CountDownLatch
1 | package com.wyj.jvm.cdl; |
手写一个Semaphore
1 | package com.wyj.jvm.semaphore; |
同步锁的本质——— 排队
同步的方式:独享锁— 单个队列窗口(如lock),共享锁— 多个队列窗口(如CountDownLatch)
抢锁的方式:插队抢(不公平锁)、先来后到抢锁(公平锁)
没抢到锁的处理方式:快速尝试多次(CAS自旋锁)、阻塞等待
唤醒阻塞线程的方式(叫号器):全部通知、通知下一个
jdk将上面的各种同步器通过模板方法模式,抽象为AbstractQueuedSynchronizer类,简称AQS(抽象队列同步器)。
1 | package com.wyj.jvm; |
基于自己写的AQS实现上面的demp MyCDL
1 | package com.wyj.jvm.cdl; |
基于自己写的AQS实现上面的demp MySemaphore
1 | package com.wyj.jvm.semaphore; |