【问题标题】:How do I get the java.concurrency.CyclicBarrier to work as expected如何让 java.concurrency.CyclicBarrier 按预期工作
【发布时间】:2011-02-01 02:33:38
【问题描述】:

我正在编写将产生两个线程的代码,然后使用 CyclicBarrier 类等待它们同步。问题是循环屏障没有按预期工作,主线程没有等待各个线程完成。这是我的代码的样子:

 class mythread extends Thread{
   CyclicBarrier barrier;
   public mythread(CyclicBarrier barrier) { 
       this.barrier = barrier;
      }

   public void run(){
            barrier.await();
       } 
 }



class MainClass{
 public void spawnAndWait(){
    CyclicBarrier barrier = new CyclicBarrier(2);
    mythread thread1 = new mythread(barrier).start();
    mythread thread2 = new mythread(barrier).start();
    System.out.println("Should wait till both threads finish executing before printing this");
  }
}

知道我做错了什么吗?或者有没有更好的方法来编写这些屏障同步方法?请帮忙。

【问题讨论】:

    标签: java concurrency multithreading cyclicbarrier


    【解决方案1】:

    像这样将Runnable 实例传递给CyclicBarrier 的构造函数。

    CyclicBarrier barrier = new CyclicBarrier(2, new Runnable() {
    
        @Override
        public void run() {
            System.out.println("Should wait till both threads finish executing before printing this");
        }
    });
    
    new mythread(barrier).start();
    new mythread(barrier).start();
    

    【讨论】:

    • 仍然不会等待两个程序都完成后再退出。有没有一种明确的方式可以说,等到 thread1 和 thread2 都完成?
    • 如果你想要一个真正的异步等待线程完成,为什么要在方法完成之前退出呢?通常,当两个线程都完成执行时,您会想做一些事情。只需将其放在 CyclicBarrier 的回调中,您就可以开始了。
    【解决方案2】:

    您正在寻找Thread.join() 方法...

    thread1.join();
    thread2.join();
    System.out.println("Finished");
    

    编辑:因为 cmets...

    如果您不想永远等待,您还可以指定等待线程死亡的最大毫秒数加上纳秒

    【讨论】:

    • 但这很棘手,因为如果 thread1.join() 没有发生,那么 thread2.join() 将无限等待。我选择 CyclicBarrier 的原因是因为它在等待过程中看起来确实是异步的。
    • @Ritesh 但如果 thread1.join 不会发生,那你还在等什么?你的用例是什么?你需要一个“线程 1 或线程 2 完成”的实现吗? pgras 展示了如何实现“等到两个都完成”
    • @Ritesh,要么你想等待 thread1thread2 退出(在这种情况下这个答案是正确的),或者你不想(在这种情况下问题中的版本是正确的。)
    • @finnw 有一种情况,你并不真的想阻塞当前线程中的所有剩余操作,但只需要在屏障之后完成其中的一些操作。
    • 我的用例是我希望 thread1.join() 和 thread2.join 都能工作。我试过了,它可以工作,当thread1抛出异常时会发生什么,join方法仍然有效吗?
    【解决方案3】:

    在执行主线程期间,您创建了另外两个线程并告诉它们互相等待。但是你没有写任何东西让你的主线程等待它们并抱怨它不等待。试试

    CyclicBarrier barrier = new CyclicBarrier(3);
    mythread thread1 = new mythread(barrier).start();
    mythread thread2 = new mythread(barrier).start();
    barrier.await(); // now you wait for two new threads to reach the barrier.
    System.out.println("Should wait till both threads finish executing before printing this");
    

    顺便说一句。除非必须,否则不要扩展 Thread 类。实现 Runnable 并将实现传递给 Thread 对象。像这样:

    class MyRunnable implements Runnable {
        public void run(){
            // code to be done in thread
        }
    }
    
    Thread thread1 = new Thread(MyRunnable);
    thread1.start();
    

    编辑
    避免扩展 Thread 的理由。
    经验法则是尽可能少的耦合。继承是类之间非常紧密的联系。如果你想改变它的一些默认行为(即覆盖一些方法)或者想访问类 Thread 的一些受保护字段,你必须从 Thread 继承。如果你不想要它,你选择更松散的耦合——实现 Runnable 并将它作为构造函数参数传递给 Thread 实例。

    【讨论】:

    • 然而,这个使它同步,而 OP 似乎想要一种真正的异步方法。
    • @Chandru:嗯,要么“应该等到两个线程都完成执行后再打印”,要么是异步的。你不能异步等待。
    • 如果我理解正确的话,他需要的是在 2 个线程完成其工作后打印该行,这可以通过在屏障动作中执行来实现。这样,如果当前线程中有其他操作不依赖于其他 2 个线程的完成,它们可以继续。顺便说一句,我不是对你投反对票的人。如果当前线程中的所有其他操作都依赖于 2 个线程的完成,这仍然是一个有效的解决方案。
    • 谢谢你们。由于这个问题,我在线程上学到的东西不多。不过是一个小问题。为什么扩展 Thread 不是一个好选择?
    【解决方案4】:

    在这种情况下,循环障碍不是正确的选择。您必须在此处使用CountDownLatch

    我假设您正在从 main 方法调用 spawnAndWait 方法。

    这不起作用的原因是 CyclicBarrier 有 2 个构造函数。要执行后期操作,您必须使用 2 参数构造函数。最重要的是要记住,主线程不会通过await方法等待;但会继续执行。但是,CyclicBarrier 构造函数中指定的线程只有在所有生成的线程都停止在屏障处时才会运行(通过await 方法)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-09-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多