Как заставить java.concurrency.CyclicBarrier работать должным образом

Я пишу код, который будет порождать два потока, а затем ждать их синхронизации с использованием класса 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");
  }
}

Любая идея, что я делаю неправильно? Или есть лучший способ написать эти методы барьерной синхронизации? Пожалуйста помоги.


person Ritesh M Nayak    schedule 26.03.2010    source источник


Ответы (4)


Во время выполнения вашего основного потока вы создаете два других потока и говорите им ждать друг друга. Но вы ничего не написали, чтобы заставить ваш основной поток ждать их и жаловаться, что он не ждет. Пытаться

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.

person Tadeusz Kopec    schedule 26.03.2010
comment
Однако этот делает его синхронным, в то время как OP, похоже, хочет действительно асинхронного подхода. - person Chandra Sekar; 26.03.2010
comment
@Chandru: Ну, либо следует дождаться завершения выполнения обоих потоков, прежде чем печатать это, либо асинхронно. Вы не можете ждать асинхронно. - person Tadeusz Kopec; 26.03.2010
comment
Если я правильно понимаю, ему нужно напечатать эту строку после того, как 2 потока завершат свою работу, чего можно добиться, выполнив это в действии барьера. Таким образом, если в текущем потоке есть другие операции, которые не зависят от завершения двух других потоков, они могут продолжаться. Кстати, я не был тем, кто проголосовал за вас. Это все еще допустимое решение, если все остальные операции в текущем потоке зависели от завершения двух потоков. - person Chandra Sekar; 26.03.2010
comment
Спасибо вам, ребята. Благодаря этому вопросу я узнал немного больше о темах. Хотя небольшой вопрос. Почему расширение Thread не является хорошим вариантом? - person Ritesh M Nayak; 26.03.2010

Передайте экземпляр 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();
person Chandra Sekar    schedule 26.03.2010
comment
Все еще не ждет завершения обеих программ перед выходом. Есть ли явный способ, которым я могу сказать, подождите, пока потоки thread1 и thread2 закончатся? - person Ritesh M Nayak; 26.03.2010
comment
Если вы хотите действительно асинхронное ожидание завершения потоков, зачем беспокоиться, если метод завершается до их завершения? Обычно вы хотите что-то сделать, когда оба потока завершают выполнение. Просто поместите это в обратный вызов CyclicBarrier, и все готово. - person Chandra Sekar; 26.03.2010

Вы ищете Метод Thread.join()...

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

EDIT: из-за комментариев...

И если вы не хотите ждать вечно, вы также можете указать максимальное количество миллисекунд плюс наносекунды для ожидания смерти потока.

person pgras    schedule 26.03.2010
comment
Но это сложно, потому что если thread1.join() не произойдет, то thread2.join() будет ждать бесконечно. Причина, по которой я выбрал CyclicBarrier, заключается в том, что он кажется действительно асинхронным в своем ожидании. - person Ritesh M Nayak; 26.03.2010
comment
@Ritesh, но если thread1.join не произойдет, чего же вы ждете? каков ваш вариант использования? вам нужна законченная реализация ИЛИ thread1 ИЛИ thread2? pgras показывает, как реализовать «подождать, пока оба не закончатся» - person Karussell; 26.03.2010
comment
@Ritesh, либо вы хотите дождаться выхода thread1 и thread2 (в этом случае этот ответ правильный), либо нет (в этом случае версия в вопросе верна). - person finnw; 26.03.2010
comment
@finnw Есть случай, когда вы действительно не хотите блокировать все оставшиеся операции в текущем потоке, но только некоторые из них нужно выполнять после барьера. - person Chandra Sekar; 26.03.2010
comment
Мой вариант использования заключается в том, что я хочу, чтобы и thread1.join(), и thread2.join работали. Я попробовал это, и это работает, что происходит, когда thread1 выдает исключение, работает ли метод соединения? - person Ritesh M Nayak; 26.03.2010
comment
@Ritesh да, thread1.join() возвращается нормально, если thread1 завершается из-за исключения. join() выбрасывает, только если вызывающий поток прерван. Он (в отличие от Future.get()) не распространяет исключения из рабочего потока в вызывающий поток. - person finnw; 26.03.2010

Циклический барьер в данном случае не лучший выбор. Вы должны использовать CountDownLatch здесь.

Я предполагаю, что вы вызываете метод spawnAndWait из основного метода.

Причина, по которой это не сработает, заключается в том, что CyclicBarrier имеет 2 конструктора. Для выполнения пост-операций вы должны использовать конструктор с двумя параметрами. Самое важное, что нужно помнить, это то, что основной поток не будет ждать методом await; но будет продолжать выполняться. Однако поток, указанный в конструкторе CyclicBarrier, будет работать только тогда, когда все порожденные потоки остановятся на барьере (методом await).

person roy2601    schedule 20.09.2011