引子:书上说AbstractQueuedSynchronizer(AQS)是构建锁和Synchronizer的框架。锁,好像大家都知道,至少自以为都知道 :)。那什么是synchronizer呢?Lock和Synchronize是什么关系?
Synchronizer,它是一个根据自身状态调节线程执行的对象。就是用来协调(多)线程执行的对象。从这个角度讲Java的内置锁就是一种synchronizer,它以互斥的可重入的方式协调/控制线程的执行。
Java的阻塞队列也是一种synchronizser。
再比如:信号量Semaphore、闭锁Latch、关卡Barrier都是不同类型的synchronizer。
信号量
Semaphore可以用来控制同时访问某资源的线程数量。把对这些资源的获取操作包装起来,获取资源前先调用信号量的acquire()申请许可,资源使用使用完毕后通过release()释放许可。对池化资源的管理一般可用信号量完成。
闭锁
英文Latch有门闩的意思,门闩就是用来把门关紧不让出入。在并发编程里也是这个意思,我们用latch这个对象禁止线程的执行,什么时候允许线程通过这个门闩呢?对于CountDownLatch来说,就是计数变为0的时候。对于FutureTask来说就是可以拿到计算结果的时候(当然也可能是计算异常了)。一个应用场景:可以把闭锁当作一个发令枪,它可以让线程等到信号后一起运行。
看!还真有这种闩数大于一的门闩。
关卡
Barrier中文就是“障碍物、栅栏”的意思,文绉绉的翻译就是“关卡”。它其实像极了闭锁,与闭锁不同的是:闭锁等待的是事件,而关卡等待的是线程。从API调用的感觉感觉上讲,Barrier是在工作线程正儿八经的工作都执行完毕后(取决于业务场景),调用barrier.await()使工作线程阻塞住,直到所有其它工作线程也都完成个各自的任务并都调用了barrier.await(),这个时候关卡就被冲破。这里等待的条件是有足够的线程调用barrier.await()。而Latch是在工作线程中调用latch.await(),等待闭锁被开启来执行。闭锁是怎样被开启的?它是依靠latch.countDown()到0后被开启的,谁countdown跟哪个线程没什么直接关系。而barrier.await()是需要实实在在的线程阻塞,这就是为什么说barrier等待的是线程,latch等的是信号。
经常使用关卡到场景是,一个任务分成n个子任务后,等待这n个子任务都完成后再做下一步工作。
CyclicBarrier的构造函数接受一个线程数以及一个关卡被突破后要执行的动作。
Exchanger是另一种关卡,用来为两个线程交换数据,当然是以线程安全的方式交换。
Reference:
https://web.mit.edu/6.005/www/fa15/classes/23-locks/
《Java并发编程实践》
https://en.wikipedia.org/wiki/Synchronization_(computer_science)
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有