8 Java中的并发工具类
8.1 CountDownLatch
- 允许一个或多个线程等待其他线程完成操作,具有join的功能,比join的功能更多。
- 构造函数
CountDownLatch(int N),int型参数作为计数器,可以是N个线程,也可以是1个线程里的N个执行步骤。CountDownLatch不可能重新初始化或者修改CountDownLatch对象内部计数器的值。
- 调用CountDownLatch的
countDown()方法时,N就会减一;CountDownLatch的await()方法会阻塞当前线程,直到N变成0。 - 当等待的线程处理过慢,不能让主线程一直等待,可以使用待指定时间的
await(long time, TimeUnit unit),等待特定时间后不再阻塞当前线程。
8.2 CyclicBarrier
-
可循环使用屏障。作用是让一组线程到达一个屏障(同步点)时被阻塞,直到最后一个线程到达屏障,屏障才会开门,所有被屏障拦截的线程才会继续运行。
-
默认构造方法
CyclicBarrier(int parties),参数表示屏障拦截的线程数量。每个线程调用await方法告诉CyclicBarrier已经达到了屏障,然后当前线程被阻塞。 -
高级构造函数
CyclicBarrier(int parties, Runnable barrierAction),用于线程到达屏障时,优先执行barrierAction,方便处理更复杂的业务场景。 -
CyclicBarrier的计数器可以使用reset()方法重置。
可以处理更复杂的业务场景,例如计算错误可以重置计数器重新执行一次。
-
其他方法如
getNumberWaiting方法可获得阻塞的线程数,isBroken()方法用来了解阻塞的线程是否被中断。 -
应用场景:用于多线程计算数据,最后合并计算结果。(主线程等待各个部分数据计算完成后,开始合并数据)
银行流水统计:一个Excel里有4个sheet,每个sheet有一年的银行流水。
import java.util.Map.Entry; import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CyclicBarrier; import java.util.concurrent.Executor; import java.util.concurrent.Executors; public class BankWaterService implements Runnable{ //创建4个屏障,处理完之后执行当前类的run方法 private CyclicBarrier c= new CyclicBarrier(4,this); //启动固定数量=4的线程池 private Executor executor = Executors.newFixedThreadPool(4); //保存4个sheet的计算数据 private ConcurrentHashMap<String, Integer> sheetBankWaterCount = new ConcurrentHashMap<>(); private void count(){ for(int i = 0; i < 4; i++){ //将任务放进线程池 executor.execute(new Runnable(){ @Override public void run(){ /*计算流水数据,过程省略,假设结果=1,放进chm中*/ sheetBankWaterCount.put(Thread.currentThread().getName(), 1); //计算完成,插入一个屏障 try{ c.await(); } catch(InterruptedException|BrokenBarrierException e){ e.printStackTrace(); } } }); } } /*当前线程的run方法,汇总sheet结果 等待之前的线程执行完成,全都调用await()方法通知之后才能开始执行。 */ @Override public void run(){ int result = 0; for(Entry<String, Integer> entry:sheetBankWaterCount.entrySet()){ result = result + entry.getValue(); } sheetBankWaterCount.put("result", result); //打印 } public static void main(String[] args){ BankWaterService service = new BankWaterService(); service.count(); } }
8.3 Semaphore
- Semaphore信号量,用来控制同时访问特定资源的线程数量。
- 用来做流量控制,特别是共用资源有限的应用场景,比如数据库链接。
Semaphore(int permits)构造方法,表示许可证数量。
Semaphore(int permits, boolean fair)指定公平还是非公平获取。- Semaphore的
acquire()/tryAcquire()方法获取一个许可证,使用完之后调用release()方法归还。acquire():响应中断,没有可用的许可证时当前线程被阻塞
acquireUninterruptibly():不响应中断
tryAcquire():尝试获取许可证(非公平),立即返回结果(非阻塞)
tryAcquire(long timeout, TimeUnit unit):尝试获取许可证(定时获取)
底层实现是AQS里面的抽象方法tryAcquireShared。
8.4 Exchanger
-
用于线程间的协作的工具类,进程线程间的数据交换。提供一个同步点,在这个同步点两个线程可以交换彼此的数据,通过exchange()方法。
线程A限制性exchange()方法,会一直等待线程B也执行exchange()方法,当两个线程都到达同步点时,交换数据。如果有一个没有执行,则会一直等待。可以使用exchange(long timeout, TimeUnit unit)设置最大等待时间。
可简单地将Exchanger对象理解为一个包含两个格子的容器,通过exchanger方法可以向两个格子中填充信息。当两个格子中的均被填充时,该对象会自动将两个格子的信息交换,然后返回给线程,从而实现两个线程的信息交换。
-
应用场景:遗传算法中需要选出两个人作为交配对象,交换两人数据,并使用交叉规则得出两个交配结果。
用于校对工作,两人录入相同数据,对数据进行校对是否一致。 -
示范:
import java.util.concurrent.Exchanger;
class ExchangerTest extends Thread {
private Exchanger<String> exchanger;
private String string;
private String threadName;
public ExchangerTest(Exchanger<String> exchanger, String string, String threadName) {
this.exchanger = exchanger;
this.string = string;
this.threadName = threadName;
}
public void run() {
try {
//exchanger.exchange(string)填充进exchanger
System.out.println(threadName + ": " + exchanger.exchange(string));
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public class Test {
public static void main(String[] args) {
/*调用同一个exchanger对象的exchange方法,进行信息通信
当两个线程均已将信息放入到exchanger对象中时,exchanger对象会将两个线程放入的信息交换,然后返回
*/
Exchanger<String> exchanger = new Exchanger<>();
ExchangerTest test1 = new ExchangerTest(exchanger, "str1", "thread1");
ExchangerTest test2 = new ExchangerTest(exchanger, "str2", "thread2");
test1.start();
test2.start();
}
}
本文深入解析Java中的四个关键并发工具类:CountDownLatch、CyclicBarrier、Semaphore和Exchanger,涵盖其基本概念、构造方法、核心功能及应用场景,帮助读者掌握线程间同步与协作的高级技巧。
196

被折叠的 条评论
为什么被折叠?



