并发编程工具 - CountDownLatch、CyclicBarrier多线程步调协调器

目录

CountDownLatch【一个线程等待多个线程的场景】

CyclicBarrier【一组线程之间互相等待,计数器可以循环利用】


CountDownLatch【一个线程等待多个线程的场景

    用于协调一个线程等待一组线程的情况,内部类Sync基于AbstractQueuedSynchronizer的共享模式实现。构造器要求传入需要协调的个数(一般一个对应一个线程,即一次countDown方法调用),与Semaphore一样传入的数值会赋值为AQS的state字段,理解完上片的Semaphore实现限流,那么当前相当于设置一个值,多个线程执行完任务后调用countDown减去一,减完之后唤醒被阻塞的等待线程。一般我们是在Tomcat等线程池中的某个线程(当前的主线程),为了提高性能,使用了线程池,而当前线程需要等待多个任务的线程执行完任务后唤醒当前的Tomcat线程。模型大致如下图:

    类似这样的场景(一个线程等待线程池任务的最后一个执行完,主线程处理各个维度的数据)之前分析过可以使用ThreadPoolExecutor#invokeAll方法、ExecutorCompletionService的take()#get()、CompletableFuture#of等情况(可见参见:并发编程工具 - CompletionService源码和项目使用并发编程工具 - CompletableFuture的API和项目使用),这些场景都可以基于CountDownLatch实现,如:

/**
 *  {@link java.util.concurrent.CountDownLatch} 协调多线程与主线程的步调
 * @author kevin
 * @date 2020/8/1 0:03
 * @since 1.0.0
 */
public class CountDownLatchDemo {

    public static void main(String[] args) throws InterruptedException {
        List<Runnable> task = new ArrayList<>();
        LinkedBlockingQueue<Integer> queue = new LinkedBlockingQueue<>();
        CountDownLatch countDownLatch = new CountDownLatch(3);
        // 执行耗时任务, 如 数据库查询, io调用等
        task.add(() -> {
            queue.add(111);
            countDownLatch.countDown();
        });
        task.add(() -> {
            queue.add(222);
            countDownLatch.countDown();
        });
        task.add(() -> {
            queue.add(333);
            countDownLatch.countDown();
        });
        OldSimpleThreadPool.executeRunnable(CREATE_ORDER, task.toArray(new Runnable[0]));
        countDownLatch.await();

        AtomicInteger total = new AtomicInteger();
        queue.iterator().forEachRemaining(num -> {
            total.addAndGet(num);
        });
        System.out.println(total.get());
    }
}

 

CyclicBarrier【一组线程之间互相等待,计数器可以循环利用】

    上面的CountDownLatch计数器一次使用完之后,下次还需要再使用时,则需要再new一个,有这样的场景就是一组线程等待再继续往下走(比如:生产消费模式、生产桌子需要配套凳子才能继续组合等)。在真实的项目中自己没有使用过,因为这样的生产消费模式很多都不是一个JVM里面,而是夸进程实现的场景。CyclicBarrier并没有像其他共享模式的AQS子类一样,使用自己的内部类,而是直接使用了ReentrantLock+Condition实现。

    下面的demo,就是一定等到三个线程有数据到了再往下走,否则就需要等待,从打印的数据可以看出,打印完叠加的666,才可能执行下一次的打印每个线程,每两次打印666中间不会有空值【即不会连续打印两次666】。如下

/**
 *  模拟三个耗时任务 循环执行, 最后的加任务就是 Barrier【屏障】
 *
 *  222
 * 333
 * 111
 * 666
 * -------------------
 * 333
 * 222
 * 111
 * 666
 * -------------------
 * 111
 * 222
 * 111
 * 333
 * 222
 * 666
 * -------------------
 * 333
 * 666
 * -------------------
 * 111
 * 222
 * 111
 * 222
 * 333
 * 666
 * -------------------
 * 333
 * 666
 * -------------------
 * 111
 * 333
 * 222
 * 222
 * 666
 * -------------------
 * 111
 * 111
 * 333
 * 222
 * 666
 * -------------------
 * 333
 * 666
 * -------------------
 *
 * @author kevin
 * @date 2020/8/1 0:26
 * @since 1.0.0
 */
public class CyclicBarrierDemo {

    public static void main(String[] args) {
        LinkedBlockingQueue<Integer> queue = new LinkedBlockingQueue<>();
        LinkedBlockingQueue<Integer> queue2 = new LinkedBlockingQueue<>();
        LinkedBlockingQueue<Integer> queue3 = new LinkedBlockingQueue<>();

        CyclicBarrier barrier = new CyclicBarrier(3, () -> {
            AtomicInteger total = new AtomicInteger();
            try {
                total.addAndGet(queue.take());
                total.addAndGet(queue2.take());
                total.addAndGet(queue3.take());

                System.out.println(total.get());
                System.out.println("-------------------");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        for (int i = 1; i < 10; i++) {
            List<Runnable> task = new ArrayList<>();
            // 执行耗时任务, 如 数据库查询, io调用等
            task.add(() -> {
                System.out.println(111);
                queue.add(111);
                try {
                    barrier.await();
                } catch (InterruptedException e) {
                } catch (BrokenBarrierException e) {
                }
            });
            task.add(() -> {
                System.out.println(222);
                queue2.add(222);
                try {
                    barrier.await();
                } catch (InterruptedException e) {
                } catch (BrokenBarrierException e) {
                }
            });
            task.add(() -> {
                System.out.println(333);
                queue3.add(333);
                try {
                    barrier.await();
                } catch (InterruptedException e) {
                } catch (BrokenBarrierException e) {
                }
            });
            OldSimpleThreadPool.executeRunnable(CREATE_ORDER, task.toArray(new Runnable[0]));

        }
    }
}

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值