java CountDownLatch 和 CyclicBarrier 的真实例子

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10156191/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 23:50:58  来源:igfitidea点击:

Real Life Examples For CountDownLatch and CyclicBarrier

javaconcurrencyconceptual

提问by Sunny Gupta

One example is given by one of our trainers when he was explaining difference between CountDownLatch and CyclicBarrier.

我们的一位培训师在解释 CountDownLatch 和 CyclicBarrier 之间的区别时举了一个例子。

CountDownLatch: Suppose a stone can be lifted by 10 people so you will wait for all 10 to come. Then only you can lift the stone.

CountDownLatch: 假设一块石头可以被 10 个人抬起,所以你将等待所有 10 个人都来。那么只有你可以举起石头。

CyclicBarrier: If you are going to a picnic, and you need to first meet at some common point from where you all will start your journey.

CyclicBarrier:如果你要去野餐,你需要先在某个共同点见面,从那里你们开始你的旅程。

If Anybody agrees with these comments please give me some details.

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

I have already read the sun API for both these classes. But I need some more explaination.

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

回答by Shailesh kumar

In a hypothetical theater:

在一个假设的剧院中:

  • It is called Mutexif only one person is allowed to watch the play.
  • It is called Semaphoreif N number of people are allowed to watch the play. If anybody leaves the Theater during the play then other person can be allowed to watch the play.
  • It is called CountDownLatchif no one is allowed to enter until every person vacates the theater. Here each person has free will to leave the theater.
  • It is called CyclicBarrierif the play will not start until every person enters the theater. Here a showman can not start the show until all the persons enter and grab the seat. Once the play is finished the same barrier will be applied for next show.
  • 如果只允许一个人观看该剧,则称为互斥锁
  • 如果允许 N 人观看该剧,则称为信号量。如果有人在演出期间离开剧院,则可以允许其他人观看演出。
  • 如果在每个人都离开剧院之前不允许任何人进入,则称为CountDownLatch。在这里,每个人都有离开剧院的自由。
  • 如果每个人都进入剧院才开始演出,则称为CyclicBarrier。在这里,表演者不能开始表演,直到所有的人都进入并抢占座位。演出结束后,下一场演出将应用相同的障碍。

Here, a person is a thread, a play is a resource.

在这里,一个人是一个线程,一个游戏是一个资源

回答by David Harkness

The key difference is that CountDownLatchseparates threads into waiters and arrivers while all threads using a CyclicBarrierperform both roles.

关键区别在于CountDownLatch将线程分为等待者和到达者,而所有使用 a 的线程CyclicBarrier都执行这两种角色。

  • With a latch, the waiters wait for the last arriving thread to arrive, but those arriving threads don't do any waiting themselves.
  • With a barrier, all threads arrive and then wait for the last to arrive.
  • 使用闩锁,等待者等待最后一个到达的线程到达,但那些到达的线程自己不做任何等待。
  • 使用屏障,所有线程到达,然后等待最后一个到达。

Your latch example implies that all ten people must wait to lift the stone together. This is not the case. A better real world example would be an exam prompter who waits patiently for each student to hand in their test. Students don't wait once they complete their exams and are free to leave. Once the last student hands in the exam (or the time limit expires), the prompter stops waiting and leaves with the tests.

您的闩锁示例意味着所有十个人都必须等待才能一起举起石头。不是这种情况。一个更好的现实世界的例子是考试提示者,他耐心地等待每个学生交出他们的考试。学生完成考试后无需等待,可以自由离开。一旦最后一名学生提交考试(或时间限制到期),提示者将停止等待并离开考试。

回答by FatherMathew

Real World Example I can see that all the answers are actually missing a real example. As in how these classes can be used in a software realm

真实世界的例子我可以看到所有的答案实际上都缺少一个真实的例子。就像如何在软件领域中使用这些类一样

  1. CountDownLatchA Multithreaded download manager. The download manager will start multiple threads to download each part of the file simultaneously.(Provided the server supports multiple threads to download). Here each thread will call a countdown method of an instantiated latch. After all the threads have finished execution, the thread associated with the countdown latch will integrate the parts found in the different pieces together into one file

  2. CyclicBarrierSame scenario as above..But assume the files are downloaded from P2P. Again multiple threads downloading the pieces. But here, suppose that you want the intergity check for the downloaded pieces to be done after a particular time interval. Here cyclic barrier plays an important role. After each time interval, each thread will wait at the barrier so that thread associated with cyclibarrier can do the integrity check. This integrity check can be done multiple times thanks to CyclicBarrier

  1. CountDownLatch一个多线程下载管理器。下载管理器会启动多个线程同时下载文件的每一部分。(前提是服务器支持多线程下载)。这里每个线程都会调用一个实例化锁存器的倒计时方法。所有线程执行完毕后,与倒计时锁存器相关联的线程会将不同部分中找到的部分整合到一个文件中

  2. CyclicBarrier与上面相同的场景..但假设文件是​​从 P2P 下载的。再次多线程下载这些片段。但在这里,假设您希望在特定时间间隔后对下载的片段进行完整性检查。在这里,循环障碍起着重要的作用。在每个时间间隔之后,每个线程都会在屏障处等待,以便与 cyclibarrier 关联的线程可以进行完整性检查。由于 CyclicBarrier,这种完整性检查可以进行多次

Please correct me if anything not proper.

如果有什么不正确的地方,请纠正我。

回答by Yves Martin

Use case 1Suppose you have split a large job into 10 small task, each one a thread. You have to wait for the 10 tasks' end from that threads before considering the job done.

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

So the main job initiator thread initializes a CountDownLatch to the number of threads used, it distributes tasks to threads and waits for the latch raises zero with awaitmethod. Each executor thread will invoke countDownat the end of its task. Finally the main thread will be waken when all threads have finished so it considers the all job is done. This scenario uses the doneSignallatch describes in the CountDownLatch javadoc.

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

Use case 2Suppose you have split a large job into a n * m tasks, distributed over n threads. m corresponds to a matrix row and you have a total to compute for each row. In that case, threads must be synchronized after each task ending so that the total for the row is compute. In that case, a CyclicBarrierinitialized with the number of threads n is used to wait for the end of each row computation (m times in fact).

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

To compare both, the CountDownLatchis supposed to be used only 1 time and a CyclicBarriercan be used as many times as the algorithm requires a synchronization point for a set of threads.

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

回答by trashgod

A CyclicBarrieris reusable, so it's more like a racing tour where everyone meets at a waypoint before proceeding on the next leg of the tour.

ACyclicBarrier是可重复使用的,因此它更像是一场赛车之旅,每个人都在一个航点相遇,然后再继续下一站之旅。

回答by V Jo

Theoretical Difference:

理论差异:

In CountDownLatch, main threads waits for other threads to complete their execution. In CyclicBarrier, worker threads wait for each other to complete their execution.

在 CountDownLatch 中,主线程等待其他线程完成它们的执行。在 CyclicBarrier 中,工作线程相互等待以完成它们的执行。

You can not reuse same CountDownLatch instance once count reaches to zero and latch is open, on the other hand CyclicBarrier can be reused by resetting Barrier, Once barrier is broken.

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

Real life example:--

现实生活中的例子:--

CountDownLatch:Consider a IT world scenario where manager divided modules between development teams (A and B) and he wants to assign it to QA team for testing only when both the teams completes their task.

CountDownLatch:考虑一个 IT 世界场景,经理在开发团队(A 和 B)之间划分模块,他希望仅在两个团队完成任务后才将其分配给 QA 团队进行测试。

Here manager thread works as main thread and development team works as worker thread. Manager thread waits for development teams thread to complete their task.

这里管理线程作为主线程工作,开发团队作为工作线程工作。经理线程等待开发团队线程完成他们的任务。

CyclicBarrier:Consider the same IT world scenario where manager divided modules between development teams (A and B). He goes on leave and asked both team to wait for each other to complete their respective task once both are done assign it to QA team for testing.

CyclicBarrier:考虑相同的 IT 世界场景,其中经理在开发团队(A 和 B)之间划分模块。他休假并要求两个团队等待对方完成各自的任务,一旦完成,将其分配给 QA 团队进行测试。

Here manager thread works as main thread and development team works as worker thread. Development team threads wait for other development team threads after completing their task.

这里管理线程作为主线程工作,开发团队作为工作线程工作。开发团队线程在完成任务后等待其他开发团队线程。

回答by Rohit Sachan

CountDownLatch:If we want all of our threads to do

CountDownLatch:如果我们想让我们所有的线程都做

something + countdown

某事+倒计时

so that other waiting(for count to reach zero) threadscan proceed, we can use countdown latch. All prior threads who actually did the countdown can go on in this situation but there is no guarantee that line processed after latch.countdown() will be after waiting for other threads to reach at latch.countdown()but it has a guarantee that other waiting threadswill only start further after latch.await() has reached zero.

以便其他等待(计数达到零)线程可以继续,我们可以使用倒计时锁存器。究竟是谁做的倒数可以去在这种情况下,但不能保证所有的线程前行后latch.countdown(处理)将等待其他线程在latch.countdown()到达后,但它有一个保证其他等待线程只会在 latch.await() 达到零后进一步启动。

CyclicBarrier:If we want all our thread to

CyclicBarrier:如果我们希望我们所有的线程

do something + await at common point + do something

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

(each await call will decrease wait time for threads to carry on further)

(每个 await 调用都会减少线程继续执行的等待时间)

CyclicBarrier functionality can be achieved by CountDownLatch only once by calling latch.countdown() followed by latch.await() by all the threads.

CyclicBarrier 功能只能通过 CountDownLatch 实现,方法是调用 latch.countdown(),然后所有线程调用 latch.await()。

but again you cant reset/reuse the countdownlatch.

但同样你不能重置/重用倒计时闩锁。

Best example where I used CyclicBarrier is to initialize multiple caches (warmed by multiple threads) and then starting further processing, and I wanted to reinitialize other caches again in Sync.

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

回答by user1849310

A cyclic barrier as the name suggests can be used in cycles. For ex: I am a company hr looking for N number of resumes from various job portal feeds. I have a skillset array containing skills sorted in order of priority. For ex java,c#,python. I want to find N resumes matching java skillset, but if I dont find the required no. of resumes, I search again on the next skillset and so on.

顾名思义,循环屏障可以在循环中使用。例如:我是一家公司人力资源部,正在从各种工作门户提要中寻找 N 份简历。我有一个包含按优先级排序的技能的技能组数组。对于前 java、c#、python。我想找到 N 份与 java 技能集匹配的简历,但如果我没有找到所需的没有。的简历,我再次搜索下一个技能,依此类推。

I create a worker each of which scans through the resumes, in the assigned job feeds. Both workers will start with the primary skillset search in their job feeds.

我创建了一个工作人员,每个工作人员都会在指定的工作源中扫描简历。两名员工都将从他们的工作供稿中的主要技能集搜索开始。

After performing the search worker will check if the N resumes were found. If found, the worker will reset the barrier and return. Else it will wait for the other worker to complete. If still N resumes were not found, the search would be resumed again, on the next skill in the skillset array. So, search can be called recursively/cyclicly without needing to create a new cyclic barrier.

执行搜索后,工作人员将检查是否找到了 N 个简历。如果找到,工人将重置屏障并返回。否则它将等待其他工人完成。如果仍然没有找到 N 个简历,搜索将再次恢复,在技能组数组中的下一个技能上。因此,可以递归/循环地调用搜索,而无需创建新的循环障碍。

回答by BigQ

Here are my observations : -----> 1. Where to use what : In a Cyclic barrier threads have to wait for other threads to give some output, and then all threads have to resume processing. So after completing its execution, each thread calls await() method and waits. When the barrier detects that all the threads have reached it, it notifies all its waiting threads and they can resume further execution. Barrier keeps track of count.

以下是我的观察: -----> 1. 在哪里使用什么: 在循环屏障中,线程必须等待其他线程给出一些输出,然后所有线程必须恢复处理。所以在完成它的执行之后,每个线程调用await() 方法并等待。当屏障检测到所有线程都到达它时,它会通知所有等待的线程,它们可以继续进一步执行。Barrier 跟踪计数。

In a CountDownLatch single main thread waits for all threads to complete. Each thread reduces the count by 1 after completing execution. After the count reaches 0, the main thread can resume further execution. Main thread keeps track of count.

在 CountDownLatch 中,单个主线程等待所有线程完成。每个线程在完成执行后将计数减 1。计数达到 0 后,主线程可以继续执行。主线程跟踪计数。

Phaser : In both ways, count of threads should be known beforehand. It is not possible to add/remove threads dynamically. If the count is not reached, thread keeping track of count will wait infinitely. With the Phaser, the number of threads can be dynamic and vary with time. It is similar to Cyclic Barrier. Thread registers with a barrier. After completing execution, it has two options. It can signal that it has arrived at the barrier point and without waiting for others it can deregister from the Phaser. Second option is that it can wait for the other registered Threads to arrive at the barrier point.

Phaser :在这两种方式中,线程数都应该事先知道。动态添加/删除线程是不可能的。如果未达到计数,跟踪计数的线程将无限等待。使用 Phaser,线程的数量可以是动态的并且随时间变化。它类似于循环屏障。线程使用屏障注册。执行完成后,有两种选择。它可以发出信号表明它已经到达屏障点,并且无需等待其他人,它就可以从 Phaser 注销。第二个选择是它可以等待其他注册的线程到达屏障点。

  1. Counts : While creating a CyclicBarrier the no of worker threads includes main thread if it is also going to wait for other threads to complete. While creating a CountDownLatch just needs to mention how many worker threads main thread will to wait to complete. In CountDownLatch there is the concept of main and worker threads and the main waiting thread is not included in count while creating a latch. Phaser can return the current count of registered Threads.

  2. Intent of await() : In CyclicBarrier :: await() all threads including main thread are equal and wait for each other. Hence await() should be given in all threads(worker as well as main thread). CountDownLatch :: await() is given in main thread only and it makes main thread wait till other worker threads make count to 0. Thus internal implementation of both await() is different. There are two concepts in Phaser :: arrival to the barrier(arrive() and arriveAndDeregister()) and waiting(awaitAdvance(phase_number)) for other threads.

  3. Parties and Waiting threads : CountDownLatch cannot give number of waiting threads but can give parties(cl.getCount()), CyclicBarrier can give no of waiting threads cb.getNumberWaiting(), and parties(cb.getParties())

  4. Work responsibilities : Worker thread in countdownlatch need to do countdown(), and await() is done by one single main thread. In cyclicBarrier worker and main threads all do only await() on each other.

  5. Reuse : CyclicBarrier can be reused. cb.await() works for new threads say t1, t2 and main. And second call to cb.await() for new threads t3,t4 and main also works. Main will wait in both calls, that is system is automatically internally resetting the count(or reset()) after barrier is exitted. CountDownLatch cannot be reused. - cl.await() works for new threads say t1, t2 and main. Main thread waits for t1, t2 to complete. But for second cl.await() call for new threads t3,t4 and main will not wait. Phaser object can be re-used again once all the Threads in the set have crossed the barrier point.

  6. After Finish Events : While creating, no finish event can be given in CountDownLatch but it can be given in CyclicBarrier.

  1. 计数:在创建 CyclicBarrier 时,工作线程的数量包括主线程,如果它还要等待其他线程完成。在创建 CountDownLatch 时只需要提及主线程将等待完成多少工作线程。在 CountDownLatch 中有主线程和工作线程的概念,主等待线程在创建闩锁时不包括在计数中。Phaser 可以返回已注册线程的当前计数。

  2. await() 的意图:在 CyclicBarrier :: await() 中,包括主线程在内的所有线程都是平等的并相互等待。因此,应在所有线程(工作线程和主线程)中给出 await()。CountDownLatch :: await() 仅在主线程中给出,它使主线程等待,直到其他工作线程计数为 0。因此 await() 的内部实现是不同的。Phaser :: 到达屏障有两个概念(arrive() 和reachAndDeregister()) 和waiting(awaitAdvance(phase_number)) 其他线程。

  3. 参与方和等待线程:CountDownLatch 不能给出等待线程数但可以给出参与方(cl.getCount()),CyclicBarrier 不能给出等待线程数 cb.getNumberWaiting() 和参与方(cb.getParties())

  4. 工作职责:countdownlatch中的Worker线程需要做countdown(),await()由一个主线程完成。在 cyclicBarrier 工作线程和主线程中,彼此都只执行 await()。

  5. 重用:CyclicBarrier 可以重用。cb.await() 适用于新线程,比如 t1、t2 和 main。对于新线程 t3、t4 和 main 对 cb.await() 的第二次调用也有效。Main 将在两个调用中等待,即系统会在屏障退出后自动在内部重置计数(或 reset())。CountDownLatch 不能重复使用。- cl.await() 适用于新线程,例如 t1、t2 和 main。主线程等待 t1、t2 完成。但是对于第二个 cl.await() 调用新线程 t3,t4 和 main 不会等待。一旦集合中的所有线程都越过屏障点,Phaser 对象就可以再次使用。

  6. 完成事件后:创建时,CountDownLatch 中不能给出完成事件,但可以在 CyclicBarrier 中给出。

class MyClass {

类我的类{

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!!" );

}

}

}

回答by java_geek

For the CyclicBarrier, One real time example that I could think of; Lets imagine there is a group of tourists on a tempo traveller. There are multiple places that are to be visited in a day. The tempo driver knows there are x number of tourists. Once the first location is reached, all the tourists go out and return back at different points of time; however the tempo and the travellers have to wait until all tourists return back. Once all of them return the driver proceeds to the next location and the same process repeats. Here, the CyclicBarrier is initialized with the number of tourists. Each tourist is like a Thread and upon returning back, they call CyclicBarrier await() so that they wait until all other tourists are back. Let me now what you think

对于 CyclicBarrier,我能想到的一个实时示例;让我们想象一下有一群游客骑着节奏旅行者。一天要游览的地方有很多。节奏驱动器知道有 x 个游客。到达第一个地点后,所有游客在不同的时间点出去和返回;然而,节奏和旅行者必须等到所有游客回来。一旦他们都回来了,司机继续到下一个位置,重复同样的过程。这里,CyclicBarrier 是用游客数量初始化的。每个游客就像一个线程,返回时,他们调用 CyclicBarrier await() 以便等待所有其他游客回来。现在告诉我你的想法