前言

昨天我们分享了多线程里面的一个计数器countDownLatch,它的主要作用是控制线程执行顺序,确保上一个操作完成后,下一个线程才能启动运行,但是某些情况下countDownLatch并不能满足我们的需求,比如执行A线程10次后,我们需要执行B线程,然后再执行A线程10次,循环往复,为了应付这样的应用场景,jdk也为我们提供了相应的解决方案——另一个计数器CyclicBarrier,今天我们就一起来看看吧。

CyclicBarrier

CyclicBarrier中文的意思是循环格栅,循环屏障,循环关卡,循环分界线,我觉得叫循环分界线应该更好理解,因为它起的作用就是分隔。它和countDownLatch一样,也是jdk1.5引入的。它的作用有点像触发器,当达到设定的数值时,我们可以触发一个操作。

这样干巴巴讲,大家可能想不来,那我们先看这样一段示例代码:

public class Example {
    static AtomicInteger count = new AtomicInteger(0);
    public static void main(String[] args) {
        AtomicInteger count2 = new AtomicInteger(0);

        CyclicBarrier cyclicBarrier = new CyclicBarrier(10, () -> {
                System.out.println("多线程循环完成" + count2.getAndIncrement());
            });
        for (int i = 0; i < 100; i++) {
            new Thread(new Task(cyclicBarrier)).start();
        }
    }

    static class Task implements Runnable {
        private final CyclicBarrier cyclicBarrier;

        public Task(CyclicBarrier cyclicBarrier) {
            this.cyclicBarrier = cyclicBarrier;
        }

        @Override
        public void run() {
            try {
                Thread.sleep(1000);
                synchronized (count) {
                    System.out.println(count.getAndIncrement());
                }
                cyclicBarrier.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
        }
    }
}

上面的代码中,我们定义了一个原子整数,初始值为0;

我们定义了一个cyclicBarrier,触发值我们设定为10,在触发操作里我们打印提示信息;

我们还定义了一个线程,在线程内部我们对原子整数count加一并打印。

然后我们在main方法中循环启动100个线程,运行上面的代码,结果大致如下:

多线程之CyclicBarrier_应用场景

根据运行结果,我们可以看出来,count每增加10,也就是启动十个线程,会触发CyclicBarrier中我们定义的操作,这个数值也就是我们在CyclicBarrier指定的触发值。

如果我们把触发值设置为5,那应该每隔5次就会打印一次,我们验证下:

多线程之CyclicBarrier_执行顺序_02

事实也确实如此,我想到这里大家应该都清楚CyclicBarrier的用法了吧。

关于CyclicBarrier的构造方法我在多说两句,CyclicBarrier有两种构造方法,但是第二种最常用,也就是我们演示的这种:

多线程之CyclicBarrier_i++_03

入参有两个,一个就是触发次数,一个就是触发操作。

另外还需要注意的是,它的await()方法和countDownLatch的方法是不一样的,在它的await()方法中,有一个--count的操作,也就是每次都会把我们设定的数值减一,直至为零,如果--count为0,它就会触发我们的barrierAction:

多线程之CyclicBarrier_i++_04

对于它的应用场景,我想大家应该能够想到很多,比如固定条数保存数据,多线程提交保存操作,然后达到固定条数提交数据库保存,当然还有很多其他的应用场景,大家可以结合自己的应用需求,好好想一想。

总结

虽然一开始我拿countDownLatch和CyclicBarrier做比较,但是事实上,它们两个不具备任何可比性,而且适用的场景也是不一样的。countDownLatch核心方法是countDown和aWait,主要用于控制线程执行顺序;CyclicBarrier主要用于有回调需求的场景,而且它的await方法也不一样,但是它们都很有用。

最后,希望大家多动手实现,多练习,毕竟学习这件事,还是实践出真知,多线程想要学的好,juc下面的类少不了,一起加油吧!