• Java并发基础-栅栏(CountDownLatch)与闭锁(CyclicBarrier)


    1. 闭锁CountDownLatch

    闭锁CountDownLatch用于线程间的同步,它可以使得一个或者多个线程等待其它线程中的某些操作完成。它有一个int类型的属性count,当某个线程调用CountDownLatch对象的await方法时,将会阻塞,直到count的值变成0;count值可以通过它的countDown的方法进行减1。 count的值在构造方法中进行指定。

    注意count的值不可以重设,因此当count的值变成0后,CountDownLatch就不再起作用了,此时再调用它的await方法将会直接返回。也就是说,对它的重复使用是没有任何意义的。

    它可以使用在以下场景:

    当count的值为1时,可以作为一个开关,所有调用它的await方法的线程都一起等待,直到开关被某个其它线程打开;

    当count的值为n时,可以使得一个或者多个线程等待其它n个线程完成了某些处理,或者某个线程执行了n次某项操作;

    典型的用法如:将一个计算过程细分成n个子计算,主线程调用CountDownLatch的await方法等待子任务计算完成;而n个子线程处理每个子任务;当每个子任务执行完成后调用countDown方法。这样子任务计算完成时主线程就可以继续往下执行了。

    注意:CountDownLatch本身是线程安全的,因此对它的方法的调用不需要再使用其它的同步机制。

    1.1 示例

    假设要主线程要等待其它一组线程执行完某个操作再继续执行,可以使用以下方式:

    直接运行,执行结果如下:

    可以看到:主线程调用latch.await方法后即等待;当所有被提交的Work作业执行完latch.countDown后,主线程继续执行,而不用等到所有子线程全部完成。

    如果将latch.countDown()方法放在每个子线程的最后,那么就意味着主线程需要等待所有子线程执行完成了。这个时候就与线程池中使用Future中的get方法类似。

    因此,在线程间的协作上,CountDownLatch可以进行更加精细的控制:某个线程可以依赖于其它线程执行部分后马上执行,而不用等待依赖的线程全部执行完成后再执行!

    1.2 CountDownLatch方法说明

    闭锁主要包含以下方法:

    a. await() throws InterruptedException

    如果count的值为0,那么该方法将直接返回;否则当前线程将会阻塞直到闭锁的count值变成0或者当前线程被中断。

    b. boolean await(long timeout, TimeUnit unit) throws InterruptedException

    如果count的值为0,那么该方法直接返回,否则将会阻塞直到闭锁的count变成0或者当前线程被中断或者超时时间到;当count变成0时返回True;否则如果超时时间到且未变成0则返回False。

    c. countDown()

    如果当前值已经是0则不会做任何事情; 否则将count的值减1;如果新值变成0的时候将会唤醒所有等待的线程;

    d. long getCount()

    返回当前的count值。

    2.栅栏CyclicBarrier

    栅栏是一种多线程之间的同步机制,它允许多个线程等待彼此直到每个线程都到达某个位置。它的名称中含有cyclic是因为它可以重复使用。

    如:有一张门有N把钥匙,需要N把钥匙组合到一起才能开门,大家商量好同时从不同地方向那张门赶去,先到的人必须要等待所有人都到达后才能打开门并进入。

    2.1 使用示例

    执行结果:

    从结果中可以看出:直到三个线程都打印了第一条消息后,所有线程才继续执行。

    注意:通过构造函数,可以传入需要等待的参与者个数。如果调用await的线程达到参与者个数,那么所有的参与者都会继续往下执行;即使参与者个数大于指定的个数。

    2.2CyclicBarrier的使用

    主要是await与reset方法

    a. await

    阻塞当前线程,直到等待所有的参与者线程都调用了这个方法。当被中断时抛出对应异常。它也可以指定超时时间,在超时时间到后直接返回。

    b. reset

    重置栅栏到初始化状态,使得它可以继续被使用。

    3.栅栏与闭锁的区别

    经过以上分析,栅栏与闭锁主要有以下区别:

    闭锁不可以重复使用,而栅栏提供了reset方法,可以重复使用。

    使用场景上:闭锁主要使用在一个或者多个线程等待某个条件的发生,这个条件一般是由其它非等待线程来进行更新;而栅栏多个线程等待的条件就是这些线程都运行到某个位置,而不能由这些线程以外的其它线程来进行控制。

    注意实际上除了重复使用外,闭锁也可以使用在栅栏的场景中,每个线程在调用闭锁的await方法前都先调用countDown(),那与直接调用栅栏的await方法可以达到同样的效果。

  • 相关阅读:
    474. Ones and Zeroes
    [LeetCode]464. Can I Win
    413. Arithmetic Slices
    numpy学习(布尔型索引)
    numpy学习(数组和标量之间的运算切片)
    numpy学习(数组的定义及基础属性)
    关于静态显示游标的遍历
    关于oracle的数组
    shutil模块
    开源库(不定义更新)
  • 原文地址:https://www.cnblogs.com/yuandluck/p/9510115.html
Copyright © 2020-2023  润新知