您的当前位置:首页正文

CountDownLatch与CyclicBarrier

2024-11-26 来源:个人技术集锦

CountDownLatch与CyclicBarrier区别

CountDownLatch

JUC包下的并发工具类CountDownLatch,CountDownLatch为递减计数器,用于控制一个线
程等待多个线程。维护一个计数器count,表示需要等待的事件数量,countdown方法递减计数
器,表示事件发生,调用await()方法的线程会一直阻塞直到计数器为零,或者等待中的线程中断,
或者等待超时

CyclicBarrier

CyclicBarrier,循环栅栏可以让一组线程等待至某个状态之后再全部同时执行,之所以叫循环是因
为所有等待线程被释放后可以调用reset()方法重用。两个比较重要的方法是await()await(time),构造方法CyclicBarrier(int, Runnable)可以让所有线程到达栅栏状态是优先执行此动作。和CountDownLatch最重要的区别就是countdown()方法后会继续执行自己的任务,而CyclicBarrier会在所有线程任务到达栅栏处才执行后续任务

区别

  • CountDownLatch 是计数器,只能使用一次,而 CyclicBarrier 的计数器提供 reset() 功能,可以多次使用
  • 对于 CountDownLatch 来说,重点是“一个线程(多个线程)等待”,而其他的 N 个线程在完成“某件事情”之后,可以终止,也可以等待。而对于 CyclicBarrier,重点是多个线程,在任意一个线程没有完成,所有的线程都必须等待
  • CountDownLatch 是计数器,线程完成一个记录一个,只不过计数不是递增而是递减,而 CyclicBarrier 更像是一个阀门,需要所有线程都到达,阀门才能打开,然后继续执行

使用Join线程等待其他线程执行完毕

例:假设有4个线程,A、B、C、D,线程 D 需要在 A、B、C 执行完之后再执行

实现:如上描述,如果想让线程 D 最后执行,结合之前的学习,我们可以采用 join() 方法来实现,比如在 A 线程调 B.join(),B 线程调用 C.join() 等等,我们来回忆一下 join() 方法:一个线程调用另一个线程的 join() 方法时,当前线程阻塞,等待被调用 join() 方法的线程执行完毕才能继续执行,这样可以保证线程执行顺序

代码实现:

/**
 * @author lwy
 * @create 2022-04-06 10:39
 */
public class JoinDemo {
    public static void main(String[] args) {
        // Thread A = new Thread(() -> System.out.println("Thread A"));
        Thread A = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("Thread A");
            }
        });

        Thread B = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("Thread B wait for Thread A");
                try {
                    A.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("Thread B");
            }
        });

        Thread C = new Thread(() -> {
            System.out.println("Thread C wait for Thread B");
            try {
                B.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("Thread C");
        });

        Thread D = new Thread(() -> {
            System.out.println("Thread D wait for Thread C");
            try {
                C.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("Thread D");
        });

        B.start();
        A.start();
        C.start();
        D.start();
    }
}

执行结果:

Thread B wait for Thread A
Thread A
Thread D wait for Thread C
Thread C wait for Thread B
Thread B
Thread C
Thread D

显然 join() 方法是可以完成 D 线程最后执行的要求,但是却失去了多线程并行执行的意义。
因为线程 A、B、C 无法完成同步运行,本质上还是串行,join() 方法其内部的实现是基于等待通知机制
所以为了解决这个问题,可以利用 CountdownLatch来实现这类通信方式

使用CountDownLatch
  • CountdownLatch 构造函数接收一个 int 类型的参数作为计数器,如果想等待 N 个点,就传入 N
  • 调用 CountdownLatch 的 countDown() 方法时,计数器 N 就会减 1
  • 在等待线程中调用 await() 方法,会进入等待状态,直到计数器 N 变为 0 后再执行

代码实现:

import java.util.concurrent.CountDownLatch;

/**
 * @author lwy
 * @create 2022-04-06 10:51
 */
public class CountdownLatch {
    public static void main(String[] args) {
        int N = 3;
        CountDownLatch countdownLatch = new CountDownLatch(N);
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("D waiting for executing!");
                try {
                    countdownLatch.await();
                    System.out.println("D start to execute");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
        for (char threadName = 'A'; threadName <= 'C'; threadName++) {
            final String tN = String.valueOf(threadName);
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println(tN + " is executing");
                    try {
                        Thread.sleep(1000);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    System.out.println(tN + " Execute finally");
                    countdownLatch.countDown();
                }
            }).start();
        }
    }
}

执行结果:

D waiting for executing!
A is executing
C is executing
B is executing
C Execute finally
B Execute finally
A Execute finally
D start to execute
场景
显示全文