CountdownLatch和CyclicBarrier

1.CountdownLatch

用来进行线程同步协作,等待所有线程完成倒计时。

其中构造参数用来初始化等待计数值,await() 用来等待计数归零, countDown( 用来让计数减一

什么时候倒计时减为0,等待的线程就可以运行了

代码:

package demo1;

import java.util.concurrent.CountDownLatch;

public class CountDownLatchTest {
    /*让主线程等待其他线程结束后在运行*/
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch countDownLatch=new CountDownLatch(3);
        new Thread(()->{
            System.out.println(Thread.currentThread()+"waiting");
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            countDownLatch.countDown();//将计数器减一
            System.out.println(Thread.currentThread()+"end");
        }).start();

        new Thread(()->{
            System.out.println(Thread.currentThread()+"waiting");
            try {
                Thread.sleep(150);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            countDownLatch.countDown();//将计数器减一
            System.out.println(Thread.currentThread()+"end");
        }).start();

        new Thread(()->{
            System.out.println(Thread.currentThread()+"waiting");
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            countDownLatch.countDown();//将计数器减一
            System.out.println(Thread.currentThread()+"end");
        }).start();

        System.out.println(Thread.currentThread()+"waiting");
        countDownLatch.await();//等待计数器减为0
        System.out.println(Thread.currentThread()+"wait  end");

    }
}

也可以使用join来实现让其他线程先运行,最后让主线程运行。有什么区别吗?

join是属于比较底层的api用起来比较繁琐,而CountDownLatch是属于高级的api,使用得场景比较多

例如用线程池去创建线程,线程是不会结束的,所以就不能用join等待线程结束再运行主线程的方法

使用线程池改进上述代码:

前三个线程去干活,最后一个线程在最后做总结


import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class CountDownLatchTest {
    /*让主线程等待其他线程结束后在运行*/
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch countDownLatch=new CountDownLatch(3);
        ExecutorService executors=Executors.newFixedThreadPool(4);

        executors.submit(()->{
            System.out.println(Thread.currentThread()+"waiting");
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            countDownLatch.countDown();//将计数器减一
            System.out.println(Thread.currentThread()+"end"+countDownLatch.getCount());
        });
        
        executors.submit(()->{
            System.out.println(Thread.currentThread()+"waiting");
            try {
                Thread.sleep(150);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            countDownLatch.countDown();//将计数器减一
            System.out.println(Thread.currentThread()+"end"+countDownLatch.getCount());
        });
        executors.submit(()->{
            System.out.println(Thread.currentThread()+"waiting");
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            countDownLatch.countDown();//将计数器减一
            System.out.println(Thread.currentThread()+"end"+countDownLatch.getCount());
        });

        executors.submit(()->{
           
            try {
                System.out.println(Thread.currentThread()+"waiting");
                countDownLatch.await();//等待计数器减为0
                System.out.println(Thread.currentThread()+"wait  end");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        });

    }
}

应用—等待多线程执行完毕

游戏等待玩家加载,玩家都加载完成后,游戏开始

知识点:

1.\r:回车,后一次打印的结果会覆盖上一个结果

2.lambd表达式只能引用常量不能引用变量(定义一个局部常量将变量赋值给他)

代码:

import java.util.Arrays;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class CountDownLatchTest2 {
    public static void main(String[] args) throws InterruptedException {
        ExecutorService service = Executors.newFixedThreadPool(10);
        String[] s = new String[10];
        CountDownLatch latch=new CountDownLatch(10);

        for (int i = 0; i < 10; i++) {
            int k = i;
            service.submit(() -> {
                for (int j = 0; j <= 100; j++) {
                    try {
                        Thread.sleep(new Random().nextInt(100));
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    s[k] = j + "%";
                    System.out.print("\r"+ Arrays.toString(s));
                }
                latch.countDown();//计数器减一
            });

        }
        latch.await();//等待计数器减为0
        System.out.println("\n游戏开始");
        service.shutdown();
    }
}

应用—同步等待多个远程调用结束

image-20211016200419893

怎么让线程池中的信息返回给主线程?

需要配合Future

image-20211016200714649

用future来实现线程的返回值

因为CountdownLatch每次使用都要重新创建一个对象,不能复用,因为它没有set方法。这个时候引入了CyclicBarrier

2.CyclicBarrier

[ saiklik 'baeria]
循环栅栏,用来进行线程协作,等待线程满足某个计数。构造时设置[计数个数」,每个线程执行到某个需
要“同步’的时刻调用await()方法进行等待,当等待的线程数满足[「计数个数」时,继续执行

它的CountdownLatch一样都是计数器,但是它是可以重用的,可以恢复到计数器最初的数值

构造方法的第二个参数是等其他的线程将加速器减为0了,它才可以运行的一个线程任务

代码:

package demo1;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class CyclicBarrierTest {
    public static void main(String[] args) {
        ExecutorService service= Executors.newFixedThreadPool(2);
        CyclicBarrier barrier=new CyclicBarrier(2,()->{
            System.out.println("task1,task2 finsh..");
        });
        for (int i = 0; i <3 ; i++) {
            service.submit(()->{
                System.out.println("task1 begin");
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                try {
                    barrier.await();//计数器减少1
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }

            });

            service.submit(()->{
                System.out.println("task2 begin");
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                try {
                    barrier.await();//计数器减少1
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }

            });
        }
        service.shutdown();
    }
}

注意:

在上述代码中 线程数要和CyclicBarrier计数要一样。

为什么呢?如果线程池数为3,计数器数为2,两个任务去执行,任务一执行1s,任务二执行三秒,那么最后两次任务一就把计数器数减为了0.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值