多线程编程-两种常用的线程计数器CountDownLatch和循环屏障CyclicBarrier

本文参考:https://mp.weixin.qq.com/s/glMjpmDHmQhcQCXYO0Faqw

倒计时CountDownLatch

CountDownLatch

CountDownLatch是一个多线程控制类,称之为“倒计时器”,它允许一个或多个线程一直等待,直到其他线程执行完后再执行。
模拟收集七龙珠

等到集齐7颗龙珠就能召唤神龙

import java.util.Random;
import java.util.concurrent.CountDownLatch;

public class MyCountDown{
    private static int maxCollectNum=7;
    //创建倒计时器,等待线程为7个
    private static CountDownLatch countDownLatch=new CountDownLatch(maxCollectNum);

    public static void main(String[] args) throws InterruptedException {
        for (int i = 1; i <= maxCollectNum; i++) {
            int index=i;
            new Thread(()->{
                try {
                    System.out.println("第"+index+"颗已收集");
                    Thread.sleep(new Random().nextInt(1000));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //每收集到一课,需要等待的颗数减1
                countDownLatch.countDown();
            }).start();
        }
        //等待检查,上述7个线程执行完毕后,执行await后面的代码
        countDownLatch.await();
        System.out.println("集齐7颗龙珠,召唤神龙");
    }
}

在这里插入图片描述
可以体会到CountdownLatch的用途,已经执行的线程需要等待未完成的线程,直到已完成线程个数达到构造器参数的数量,然后执行后面的程序。

特性

  • 构造函数CountDownLatch countDownLatch=new CountDownLatch(maxCollectNum);maxCollectNum表示需要等待执行完毕的线程数量;
  • 在每一个线程执行完后都要执行countDownLatch.countDown();用来计数;
  • 只有所有线程执行完毕后,才会执行countDownLatch.await();后的代码;
  • 可以看出上面代码中阻塞的是主线程;

作用

CountDownLatch是在1.5之后被引入的,位于java.util,concurrent包下,这个类能够使一个线程等待其他线程完成各自的工作后在执行。

执行流程

  • 构造器中的计数值实际上就是闭锁需要等待的线程数量,这个只能被设置一次,而且没有提供任何机制起重新设置这个值;
  • 与CountdownLatch第一次交互是主线程等待其他线程,必须启动其他线程后调用await方法,这样主线程就会在这个方法上阻塞,直到其他线程完成;
  • 其他线程必须引用闭锁对象,用来通知对象已经完成任务,这种机制通过countDownLatch.countDown();来完成。

使用场景

  • 实现最大并行性;
  • 开始执行前等待n个线程完成各自任务

循环屏障CycliBarrier

和CountdownLatch类似,但是功能更强大,字面意思是可循环使用的屏障,而CountdownLatch可以认为只有一个屏障。
继续七龙珠问题,需要先召集七个法师在同一个地点,等人齐了之后(第一个屏障),在分别去不同的地方寻找七龙珠,等到七个法师都找到七龙珠回来后(第二个屏障),在召唤神龙。

import java.util.Random;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CyclicBarrier;

public class MyCountDown{
    private static int maxCollectNum=7;

    public static void main(String[] args) throws InterruptedException {
        //设置第一个屏障,等待集齐7位法师
        CyclicBarrier cyclicBarrier = new CyclicBarrier(maxCollectNum, new Runnable() {
            @Override
            public void run() {
                System.out.println("7个法师召集完毕,去往不同地方寻找龙珠");
                sumonDragon();
            }
        });
        for (int i = 1; i <= maxCollectNum; i++) {
            int index=i;
            new Thread(()->{
                try {
                    System.out.println(Thread.currentThread().getName()+"召唤第"+index+"个法师");
                    //创建的子线程在此会被阻塞,直到线程数量达到指定的7个,然后执行run中的程序
                    cyclicBarrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }

    private static void sumonDragon() {
        //设置第二个平屏障,等待七位法师收集完7颗龙珠,召唤神龙
        CyclicBarrier cyclicBarrier = new CyclicBarrier(maxCollectNum, new Runnable() {
            @Override
            public void run() {
                System.out.println("集齐七颗龙珠,召唤神龙");
            }
        });
        for (int i = 0; i < maxCollectNum; i++) {
            int index=i;
            new Thread(()->{
                System.out.println("第"+index+"龙珠已被找到");
                try {
                //创建的子线程在此会被阻塞,直到线程数量达到指定的7个,然后执行run中的程序
                    cyclicBarrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

在这里插入图片描述

使用CyclicBarrier.reset()可知,可以使CyclicBarrier回到最初始状态;

CylicBarrier和CountDownLatch的区别

  • CountDownLatch计数器只能使用一次,而CylicBarrier的计数器可以使用reset方法重置。
  • CyclicBarrier还有其他有用的方法,如getNumberWaiting获得阻塞线程的数量;
  • CountdownLatch会阻塞主线程,CyclicBarrier不会阻塞主线程,只会阻塞子线程;

猜你喜欢

转载自blog.csdn.net/mashaokang1314/article/details/88119555