目录
1.CountDownLatch和CyclicBarrier
1.CountDownLatch和CyclicBarrier
CountDownLatch是一个同步的辅助类,允许一个或多个线程,等待其他一组线程完成操作,再继续执行。
CyclicBarrier是一个同步的辅助类,允许一组线程相互之间等待,达到一个共同点,再继续执行。
CyclicBarrier和CountDownLatch都位于java.util.concurrent 这个包下
CountDownLatch | CyclicBarrier |
减计数方式 | 加计数方式 |
计算为0时释放所有等待的线程 | 计数达到指定值时释放所有等待线程 |
计数为0时,无法重置 | 计数达到指定值时,计数置为0重新开始 |
调用countDown()方法计数减一,调用await()方法只进行阻塞,对计数没任何影响 | 调用await()方法计数加1,若加1后的值不等于构造方法的值,则线程阻塞 |
不可重复利用 | 可重复利用 |
个人理解:CountDownLatch:我把他理解成倒计时锁:倒计时完成开始执行下一个任务
场景还原:一年级期末考试要开始了,监考老师发下去试卷,然后坐在讲台旁边玩着手机等待着学生答题,有的学生提前交了试卷,并约起打球了,等到最后一个学生交卷了,老师开始整理试卷,贴封条,下班,陪老婆孩子去了。
补充场景:我们在玩LOL英雄联盟时会出现十个人不同加载状态,但是最后一个人由于各种原因始终加载不了100%,于是游戏系统自动等待所有玩家的状态都准备好,才展现游戏画面。
个人理解:CyclicBarrier:可看成是个障碍,所有的线程必须到齐后才能一起通过这个障碍
场景还原:以前公司组织户外拓展活动,帮助团队建设,其中最重要一个项目就是全体员工(包括女同事,BOSS)在完成其他项目时,到达一个高达四米的高墙没有任何抓点,要求所有人,一个不能少的越过高墙,才能继续进行其他项目。
1.countDownlatch
public class countDownlatchTest implements Runnable{
private int id;
private CountDownLatch latch;
public countDownlatchTest(int id,CountDownLatch latch){
this.id = id;
this.latch = latch;
}
@Override
public void run() {
synchronized (this){
latch.countDown();//计数减一
System.out.println("线程组任务"+id+"结束,其他任务继续");
}
}
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch =new CountDownLatch(5);
for (int a=0;a<5;a++){
Thread thread=new Thread(new countDownlatchTest(a,countDownLatch));
thread.start();
}
countDownLatch.await();//等待全部执行完成才执行之后的代码
System.out.println("线程执行结束。。。。");
}
}
1.CyclicBarrier
/**
* CyclicBarrier:可看成是个障碍,所有的线程必须到齐后才能一起通过这个障碍
*/
public class CyclicBarrierTest {
public static void main(String[] args) {
int N = 4;
CyclicBarrier barrier = new CyclicBarrier(N);
for(int i=0;i<N;i++)
new Writer(barrier).start();
}
static class Writer extends Thread{
private CyclicBarrier cyclicBarrier;
public Writer(CyclicBarrier cyclicBarrier) {
this.cyclicBarrier = cyclicBarrier;
}
@Override
public void run() {
System.out.println("线程"+Thread.currentThread().getName()+"正在写入数据...");
try {
Thread.sleep(5000); //以睡眠来模拟写入数据操作
System.out.println("线程"+Thread.currentThread().getName()+"写入数据完毕,等待其他线程写入完毕");
cyclicBarrier.await();//等待所有线程都完成才能执行以后的代码(同时放出多个线程万箭齐发)
} catch (InterruptedException e) {
e.printStackTrace();
}catch(BrokenBarrierException e){
e.printStackTrace();
}
System.out.println("线程"+Thread.currentThread().getName()+"所有线程写入完毕,继续处理其他任务...");
}
}
}
2. 控制并发线程数的Semaphore
Semaphore(信号量)是用来控制同时访问特定资源的线程数量,它通过协调各个线程,以保证合理的使用公共资源。
应用:Semaphore可以用于做流量控制,特别是公用资源有限的应用场景,比如数据库连接。假如有一个需求,要读取几万个文件的数据,因为都是IO密集型任务,我们可以启动几十个线程并发地读取,但是如果读到内存后,还需要存储到数据库中,而数据库的连接数只有10个,这时我们必须控制只有10个线程同时获取数据库连接保存数据,否则会报错无法获取数据库连接。这个时候,就可以使用Semaphore来做流量控制,
public class SemaphoreTest {
private static final int THREAD_COUNT = 30;
private static ExecutorService threadPool = Executors.newFixedThreadPool(THREAD_COUNT);
private static Semaphore s = new Semaphore(10);// 每次只有10个线程可以访问
public static void main(String[] args) {
for (int i = 0; i< THREAD_COUNT; i++) {
threadPool.execute(new Runnable() {
@Override
public void run() {
try {
s.acquire();//获取一个许可证(
System.out.println("save data");
s.release();//归还许可证。
} catch (InterruptedException e) {
}
}
});
}
threadPool.shutdown();
}
}
3.线程间交换数据的Exchanger
Exchanger(交换者)是一个用于线程间协作的工具类。Exchanger用于进行线程间的数据交换。它提供一个同步点,在这个同步点,两个线程可以交换彼此的数据。这两个线程通过 exchange方法交换数据,如果第一个线程先执行exchange()方法,它会一直等待第二个线程也执行exchange方法,当两个线程都到达同步点时,这两个线程就可以交换数据,将本线程生产出来的数据传递给对方。