Java 倒计时锁存器和CyclicBarrier的实际示例

Java 倒计时锁存器和CyclicBarrier的实际示例,java,concurrency,conceptual,Java,Concurrency,Conceptual,我们的一位培训师在解释倒计时闩锁和骑自行车的人之间的区别时给出了一个例子 当前位置假设一块石头可以被10个人举起,那么你将等待所有10个人的到来。那么只有你才能举起石头 当前位置如果你要去野餐,你首先需要在某个共同点会面,从那里开始你的旅程 如果有人同意这些评论,请给我一些细节 我已经阅读了这两个类的sunapi。但是我需要更多的解释。用例1假设您将一个大任务分成10个小任务,每个任务一个线程。在考虑任务完成之前,您必须等待该线程的10个任务结束 因此,主作业启动器线程根据使用的线程数初始化倒计

我们的一位培训师在解释倒计时闩锁和骑自行车的人之间的区别时给出了一个例子

当前位置假设一块石头可以被10个人举起,那么你将等待所有10个人的到来。那么只有你才能举起石头

当前位置如果你要去野餐,你首先需要在某个共同点会面,从那里开始你的旅程

如果有人同意这些评论,请给我一些细节


我已经阅读了这两个类的sunapi。但是我需要更多的解释。

用例1假设您将一个大任务分成10个小任务,每个任务一个线程。在考虑任务完成之前,您必须等待该线程的10个任务结束

因此,主作业启动器线程根据使用的线程数初始化倒计时锁存器,它将任务分配给线程,并使用
await
方法等待锁存器调零。每个执行器线程将在其任务结束时调用
倒计时
。最后,当所有线程都完成时,主线程将被唤醒,因此它认为整个任务都完成了。此场景使用CountDownLatch javadoc中描述的
doneSignal
latch

用例2假设您将一个大作业拆分为n*m个任务,分布在n个线程上。m对应于一个矩阵行,您需要计算每行的总数。在这种情况下,必须在每个任务结束后同步线程,以便处理行的总数。在这种情况下,用线程数n初始化的
CyclicBarrier
用于等待每行计算的结束(实际上是m次)


两者比较,,
CountDownLatch
应该只使用一次,并且只要算法需要一组线程的同步点,
CyclicBarrier
可以使用多次。

关键区别在于
CountDownLatch
将线程分为等待线程和arriver,而所有线程都使用
CyclicBarrier
扮演这两个角色


  • 使用闩锁,服务员等待最后一个到达的线程到达,但是那些到达的线程自己不做任何等待
  • 使用屏障时,所有线程都会到达,然后等待最后一个线程到达

您的闩锁示例意味着所有十个人必须等待一起举起石头。事实并非如此。一个更好的现实例子是考试提词器,它耐心地等待每个学生交考试。学生完成考试后不必等待,可以自由离开。一旦最后一个学生交上考试(或时间限制到期),提示器就会停止等待并离开考试。

A
CyclicBarrier
是可重复使用的,因此它更像是一次赛车之旅,每个人都在一个航路点集合,然后再继续下一段旅程。

countdownlock: 如果我们想让所有的线程都执行

某物+倒计时

为了让其他等待(计数达到零)线程可以继续,我们可以使用倒计时锁存器。在这种情况下,所有之前实际执行倒计时的线程都可以继续,但不能保证在闩锁后处理行。倒计时()将在等待其他线程到达闩锁后进行。倒计时() 但是它保证了其他正在等待的线程只会在latch.await()达到零后才进一步启动

自行车承运人: 如果我们想让所有的线

做某事+在共同点等待+做某事

(每个等待调用将减少线程进一步执行的等待时间)

所有线程调用lock.countdown(),然后调用lock.await(),通过CountDownLatch只能实现一次CyclicBarrier功能

但同样,您不能重置/重用倒计时闩锁


我使用CyclicBarrier的最佳示例是初始化多个缓存(由多个线程预热),然后开始进一步处理,我想再次同步重新初始化其他缓存。

在一个假设的场景中:

  • 如果只允许一个人观看比赛,则称为互斥
  • 如果允许N人观看比赛,则称为信号灯 玩如果有人在演出期间离开剧院,那么其他人 可以让他看这出戏
  • 如果在之前不允许任何人进入,则称之为倒计时闩锁 每个人都离开剧院。在这里,每个人都有自由选择的意愿 离开剧院
  • 如果比赛要到每周一次才开始,则称之为“自行车马车” 一个人进入剧院。在这里,表演者只有在 所有的人都进来抓住座位。这出戏一看完也一样 下一场演出将使用屏障

在这里,一个人是一根线,一出戏是一种资源。

理论上的区别:

在倒计时闩锁中,主线程等待其他线程完成其执行。 在CyclicBarrier中,工作线程彼此等待以完成其执行

一旦计数达到零并且闩锁打开,您就不能重用同一个CountDownLatch实例,另一方面,一旦屏障被打破,CyclicBarrier可以通过重置屏障来重用

现实生活示例:---


<强> NouttListCH:考虑IT世界场景,其中管理器在开发团队(A和B)之间划分模块,并且他想将其分配给QA团队,以便在两个团队完成任务时进行测试。

在这里,管理线程充当主线程,开发团队充当工作线程。Manager线程等待开发团队线程完成其任务

<强>循环障碍:< /强>考虑管理者D相同的IT世界场景

static class MyThread implements Runnable
{

    long waitTime;
    CyclicBarrier cyclicBarrier;
    CountDownLatch countdownlatch;
    Phaser phaser;


    MyThread(  long waitTime, CyclicBarrier cyclicBarrier, CountDownLatch countdownlatch, Phaser phaser){
        this.waitTime = waitTime;
        this.cyclicBarrier = cyclicBarrier;
        this.countdownlatch = countdownlatch;
        this.phaser = phaser;
        this.phaser.register(); //Note additional step here
    }

    @Override
    public void run() {

            try {

                Thread.sleep(waitTime);

                // Diff 4 -----> countdownlatch worker threads need to do countdown and await is done by one single main thread
                //, cyclicBarrier worker threads await on each other
                countdownlatch.countDown(); 
                cyclicBarrier.await();
                phaser.arriveAndAwaitAdvance();

                System.out.println("cyclicBarrier :: " + 
                        ", name :: " + Thread.currentThread().getName() 
                        + ", parties :: " + cyclicBarrier.getParties() 
                        + ", waiting :: "+ cyclicBarrier.getNumberWaiting()); 

                System.out.println("countdownlatch :: " + 
                            "name :: " + Thread.currentThread().getName()  +
                         ", parties :: "+countdownlatch.getCount() +
                         ", waiting :: " + "No method!!" ); 
                System.out.println("phaser :: " + 
                        "name :: " + Thread.currentThread().getName()  +
                     ", parties :: "+phaser.getRegisteredParties() +
                     ", phase :: " + phaser.getPhase()); 

                phaser.arriveAndAwaitAdvance();
                System.out.println("phaser :: " + 
                        "name :: " + Thread.currentThread().getName()  +
                     ", parties :: "+phaser.getRegisteredParties() +
                     ", phase :: " + phaser.getPhase());

                phaser.arriveAndAwaitAdvance();
                System.out.println("phaser :: " + 
                        "name :: " + Thread.currentThread().getName()  +
                     ", parties :: "+phaser.getRegisteredParties() +
                     ", phase :: " + phaser.getPhase());
                phaser.arriveAndDeregister(); 
                if (phaser.isTerminated()) { 
                    System.out.println("Phaser is terminated"); 
                } 

            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }

    }

}

public static class MyCBFinishEvent implements Runnable{

    public void run() {

           System.out.println("All threads have reached common barrier point "
                        + ", CyclicBarrrierFinishEvent has been triggered");
           System.out.println("You can update shared variables if any");
    }

}

public static void main(String [] args) throws InterruptedException, BrokenBarrierException{
    //Diff 1 ----- > No finish event can be given in CountDownLatch
    //Diff 5 ------> CyclicBarrier no of worker threads includes main thread, 
    //CountDownLatch is just how many threads, the main waiting thread is not included in count.
    CyclicBarrier cb = new CyclicBarrier(3, new MyCBFinishEvent());
    CountDownLatch cl = new CountDownLatch(2);
    Phaser ph = new Phaser();

    //Diff 2 ----> CountDownLatch cant give num of waiting threads, CyclicBarrier can getNumberWaiting threads
     System.out.println("Start CyclicBarrier - parties :: "+cb.getParties() + ", waiting :: " + cb.getNumberWaiting());
     System.out.println("Start CountDownLatch - parties :: "+cl.getCount() + ", waiting :: " + "No method!!" );

    Runnable t1 = new Thread(new MyThread( 10000, cb, cl, ph));
    Runnable t2 = new Thread(new MyThread( 5000, cb, cl,ph));
     Thread tt1 = new Thread(t1, "t1");
     Thread tt2 = new Thread(t2, "t2");
     tt1.start();
     tt2.start();

     //Diff 6 ---- > await meaning Main waits for t1 and t2 to complete, 
     //CyclicBarrier all are equal. each thread including main thread, if it wants to wait has to do await. 
     //CountDownLatch concept of waiting and workers. main thread await waits till other worker threads make count to 0.
     cb.await();
     cl.await();

     System.out.println("End CyclicBarrier call 1 - parties :: "+cb.getParties() + ", waiting :: " + cb.getNumberWaiting());
     System.out.println("End CountDownLatch call 1 - parties :: "+cl.getCount() + ", waiting :: " + "No method!!" );

     System.out.println("main start created t3, t4 - parties :: "+cl.getCount() + ", waiting :: " + "No method!!" );

     Runnable t3 = new Thread(new MyThread( 6000, cb, cl,ph));
        Runnable t4 = new Thread(new MyThread( 100, cb, cl,ph));
         Thread tt3 = new Thread(t3, "t3");

         Thread tt4 = new Thread(t4, "t4");

         tt3.start();
         tt4.start();

        //Diff -3 -----> 
         //CyclicBarrier - can be reused, main thread waited for t3, t4 to complete.
         //CountDownLatch - for first cl.await(), main waited... second cl.await() call main did not wait!!! 
         cb.await();
         cl.await();


         System.out.println("End main - parties :: "+cb.getParties() + ", waiting :: " + cb.getNumberWaiting());
         System.out.println("end main parties :: "+cl.getCount() + ", waiting :: " + "No method!!" );

}
CountDownLatch start  = new CountDownLatch(1); // Start signal - will be always 1 only once All Horses Reach at Starting Point signal for START RACE.

CountDownLatch finish = new CountDownLatch(horses_Count); // Number of horses participating in Race nothing but Threads/Horses Count.