CountDownLatch和CyclicBarrier原理的理解与实际的区别

看了各种资料和书,大家一致的意见都是CountDownLatch是计数器,只能使用一次,而CyclicBarrier的计数器提供reset功能,可以多次使用;但是我认为这只是一种比较笼统的区别,从javadoc中我们可以获取到这样描述:

CyclicBarrier:一种同步辅助工具,允许一组线程都等待彼此到达一个共同的障碍点。cyclicbarrier在涉及固定大小的线程组的程序中很有用,这些线程偶尔必须等待对方。这个屏障被称为(循环的),因为它可以在等待的线程被释放后重新使用。

CountDownLatch: 一种同步辅助工具,允许一个或多个线程等待一组在其他线程中执行的操作完成。倒计时锁存器用给定的计数初始化。由于调用倒计时方法,await方法阻塞直到当前计数达到零,之后释放所有等待线程,并立即返回任何后续的await调用。这是一种一次性现象——计数无法重置。如果需要重置计数的版本,请考虑使用CyclicBarrier。

从javadoc的定义中我们可以看出

CountDownLatch侧重于一个或者多个线程等待其他线程中执行的操作完成。应用场景如:背景想要获取用户最大的数据集合(用户基本数据、订单、购物车信息),但是用户基本数据、订单、购物车信息都在不同的微服务中,这种情况可以使用,主线程在获取基本数据的同时,将订单信息、购物信息另开线程异步获取,最终主线程会等待其他线程获取的消息进行合集返回

CyclicBarrier更加侧重:所有的线程都在await()的,等待最后一个计数之后,所有的线程统一执行,感觉可以用在压测上。

套路上来讲应该上一下demo测试一下:但是感觉依然没什么用。。。。。。

public class CountDownLatchTest {

    public static void main(String[] args) throws InterruptedException {
        CountDownLatch latch = new CountDownLatch(4);
        for(int i = 0; i < latch.getCount(); i++){
            new Thread( ()->{
                Random rand = new Random();
                int randomNum = rand.nextInt((3000 - 1000) + 1) + 1000;
                try {
                    TimeUnit.MILLISECONDS.sleep(randomNum);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+" CountDownLatch Test "+((double)randomNum/1000)+"s");
                latch.countDown();
            }, "CountDownLatch Test "+i).start();
        }
        System.out.println("CountDownLatch start");
        latch.await();
        System.out.println("CountDownLatch end");
    }
}
public class CyclicBarrierTest {
    public static void main(String[] args) {
        CyclicBarrier barrier = new CyclicBarrier(3);
        for(int i = 0; i < barrier.getParties(); i++){
            new Thread(()->{
                Random rand = new Random();
                int randomNum = rand.nextInt((3000 - 1000) + 1) + 1000;
                try {
                    TimeUnit.MILLISECONDS.sleep(randomNum);
                    System.out.println(Thread.currentThread().getName() + ", CyclicBarrier Test "+((double)randomNum/1000)+"s");
                    barrier.await();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }, "队友"+i).start();
        }
        System.out.println("CyclicBarrier Test is finished.");
    }
}

CyclicBarrier源码

来了解一下CyclicBarrier的套路:源码一路了然

private int dowait(boolean timed, long nanos) throws InterruptedException, BrokenBarrierException, TimeoutException {
        //使用了ReentrantLock锁
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            final CyclicBarrier.Generation g = generation;
            if (g.broken)
                throw new BrokenBarrierException();
            if (Thread.interrupted()) {
                breakBarrier();
                throw new InterruptedException();
            }
            //count值为new CyclicBarrier(3)中设置的3 可以看一下他的构造方法
            int index = --count;
            //如果index减到0 我们开始执行下面的代码 从构造方法中可以知道 barrierCommand为null
            //所以直接执行nextGeneration(); 他的核心代码就是trip.signalAll(); 唤醒所有的线程
            //其中trip为 Condition trip = lock.newCondition();
            if (index == 0) {  // tripped
                boolean ranAction = false;
                try {
                    final Runnable command = barrierCommand;
                    if (command != null)
                        command.run();
                    ranAction = true;
                    nextGeneration();
                    return 0;
                } finally {
                    if (!ranAction)
                        breakBarrier();
                }
            }

            // 如果index不为0  trip.await(); 等待唤醒
            for (;;) {
                try {
                    if (!timed)
                        trip.await();
                    else if (nanos > 0L)
                        nanos = trip.awaitNanos(nanos);
                } catch (InterruptedException ie) {
                    if (g == generation && ! g.broken) {
                        breakBarrier();
                        throw ie;
                    } else {
                        Thread.currentThread().interrupt();
                    }
                }
            }
        } finally {
            lock.unlock();
        }
    }

总结一下就是:如果CyclicBarrier中的index没有减到0就在await(),减到0是调用signalAll()唤醒所有线程执行任务;

CountDownLatch的源码

下面的源码可以知道最中就是调用AQS获取status值

CountDownLatch和CyclicBarrier原理的理解与实际的区别_第1张图片

从CountDownLatch和CyclicBarrier的源码中可以知道,他们两个的实现方式不一样,一个是利用AQS获取status值,另一种是通过ReentrantLock的唤醒机制来实现。

CountDownLatch来说,重点是“一个线程等待(多个线程)”,而其他的N个线程在完成“某件事情”之后,可以终止,也可以等待。而对于CyclicBarrier,重点是多个线程,在任意一个线程没有完成,所有的线程都必须互相等待,然后继续一起执行

 

 

循环屏障CyclicBarrier以及和CountDownLatch的区别

你可能感兴趣的:(面试,java,多线程,锁)