多线程安全——JUC工具汇总

本文详细介绍了Java并发编程中的一些重要工具类,包括LongAdder、CountDownLatch、CyclicBarrier、Phaser、Semaphore和Exchanger。在高并发场景下,这些工具能够帮助开发者实现线程间的同步和协作,提升程序性能。比如,LongAdder在性能上优于AtomicLong,CountDownLatch用于线程等待,CyclicBarrier和Phaser用于多线程分阶段同步,Semaphore用于控制并发访问资源的数量,Exchanger则支持线程间的数据交换。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

LongAdder

JDK1.8时,java.util.concurrent.atomic包中提供了一个新的原子类:LongAdder
根据Oracle官方文档的介绍,LongAdder在高并发的场景下会比它的前辈————AtomicLong 具有更好的性能,代价是消耗更多的内存空间:

参考文献

https://segmentfault.com/a/1190000015865714

CountDownLatch

CountDownLatch是一个同步工具类,位于 java.util.concurrent 包下,利用它可以实现类似计数器的功能。它允许一个或多个线程一直等待,直到其他线程执行完后再执行。例如,应用程序的主线程希望在负责启动框架服务的线程已经启动所有框架服务之后执行。

用法

CountDownLatch典型用法1:某一线程在开始运行前等待n个线程执行完毕。将CountDownLatch的计数器初始化为n new CountDownLatch(n) ,每当一个任务线程执行完毕,就将计数器减1 countdownlatch.countDown(),当计数器的值变为0时,在CountDownLatch上 await() 的线程就会被唤醒。一个典型应用场景就是启动一个服务时,主线程需要等待多个组件加载完毕,之后再继续执行。

CountDownLatch典型用法2:实现多个线程开始执行任务的最大并行性。注意是并行性,不是并发,强调的是多个线程在某一时刻同时开始执行。类似于赛跑,将多个线程放到起点,等待发令枪响,然后同时开跑。做法是初始化一个共享的CountDownLatch(1),将其计数器初始化为1,多个线程在开始执行任务前首先 coundownlatch.await(),当主线程调用 countDown() 时,计数器变为0,多个线程同时被唤醒。

参考文献

https://segmentfault.com/a/1190000015886497?utm_source=sf-similar-article

CountDownLatch用法详解_停住了过去,却停不住时间-CSDN博客_countdownlatch用法

CyclicBarrier

CyclicBarrier是一个辅助同步器类,在JDK1.5时随着J.U.C一起引入。

这个类的功能和CountDownLatch有些类似。CountDownLatch是一个倒数计数器,在计数器不为0时,所有调用await的线程都会等待,当计数器降为0,线程才会继续执行,且计数器一旦变为0,就不能再重置了。

顾名思义,CyclicBarrier是一个可以循环使用的栅栏,它做的事情就是:
让线程到达栅栏时被阻塞(调用await方法),直到到达栅栏的线程数满足指定数量要求时,栅栏才会打开放行。

CyclicBarrier与CountDownLatch的区别

这两个类都可以实现一组线程在到达某个条件之前进行等待,它们内部都有一个计数器,当计数器的值不断的减为0的时候所有阻塞的线程将会被唤醒。CountDownLatch主要用在一个线程等待多个线程执行完毕的情况,而CyclicBarrier用在多个线程互相等待执行完毕的情况

有区别的是CyclicBarrier的计数器由自己控制,而CountDownLatch的计数器则由使用者来控制,在CyclicBarrier中线程调用await方法不仅会将自己阻塞还会将计数器减1,而在CountDownLatch中线程调用await方法只是将自己阻塞而不会减少计数器的值。

另外,CountDownLatch只能拦截一轮,而CyclicBarrier可以实现循环拦截。一般来说用CyclicBarrier可以实现CountDownLatch的功能,而反之则不能。
 

参考文献

https://segmentfault.com/a/1190000015888316?utm_source=sf-similar-article

Phaser

PhaserJDK1.7开始引入的一个同步工具类,适用于一些需要分阶段的任务的处理。它的功能与 CyclicBarrierCountDownLatch有些类似,类似于一个多阶段的栅栏。

差异

同步器作用
CountDownLatch倒数计数器,初始时设定计数器值,线程可以在计数器上等待,当计数器值归0后,所有等待的线程继续执行
CyclicBarrier循环栅栏,初始时设定参与线程数,当线程到达栅栏后,会等待其它线程的到达,当到达栅栏的总数满足指定数后,所有等待的线程继续执行
Phaser多阶段栅栏,可以在初始时设定参与线程数,也可以中途注册/注销参与者,当到达的参与者数量满足栅栏设定的数量后,会进行阶段升级(advance)

参考文献

https://segmentfault.com/a/1190000015979879?utm_source=sf-similar-article

Semaphore

Semaphore,又名信号量,这个类的作用有点类似于“许可证”。有时因为一些原因需要控制同时访问共享资源的最大线程数量,比如出于系统性能的考虑需要限流,或者共享资源是稀缺资源,需要有一种办法能够协调各个线程,以保证合理的使用公共资源。

Semaphore维护了一个许可集,其实就是一定数量的“许可证”。
当有线程想要访问共享资源时,需要先获取(acquire)的许可;如果许可不够了,线程需要一直等待,直到许可可用。当线程使用完共享资源后,可以归还(release)许可,以供其它需要的线程使用。

另外,Semaphore支持公平/非公平策略,这和ReentrantLock类似。

参考文献

https://segmentfault.com/a/1190000015918459?utm_source=sf-similar-article

Exchanger

Exchanger——交换器,是JDK1.5时引入的一个同步器,从字面上就可以看出,这个类的主要作用是交换数据。

Exchanger有点类似于CyclicBarrierCyclicBarrier是一个栅栏,到达栅栏的线程需要等待其它一定数量的线程到达后,才能通过栅栏。

Exchanger可以看成是一个双向栅栏,如下图:

Thread1线程到达栅栏后,会首先观察有没其它线程已经到达栅栏,如果没有就会等待,如果已经有其它线程(Thread2)已经到达了,就会以成对的方式交换各自携带的信息,因此Exchanger非常适合用于两个线程之间的数据交换 

参考文献

https://segmentfault.com/a/1190000015963932?utm_source=sf-similar-article

LockSupport

LockSupport类,是JUC包中的一个工具类,是用来创建锁和其他同步类的基本线程阻塞原语。(Basic thread blocking primitives for creating locks and other synchronization classes

LockSupport类的核心方法其实就两个:park()unark(),其中park()方法用来阻塞当前调用线程,unpark()方法用于唤醒指定线程。

这其实和Object类的wait()和signial()方法有些类似,但是LockSupport的这两种方法从语意上讲比Object类的方法更清晰,而且可以针对指定线程进行阻塞和唤醒。

LockSupport类使用了一种名为Permit(许可)的概念来做到阻塞和唤醒线程的功能,可以把许可看成是一种(0,1)信号量(Semaphore),但与 Semaphore 不同的是,许可的累加上限是1。
初始时,permit为0,当调用unpark()方法时,线程的permit加1,当调用park()方法时,如果permit为0,则调用线程进入阻塞状态。

参考文献

https://segmentfault.com/a/1190000015562456

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值