2010-03-26 14 views
7

Piszę kod, który spawnuje dwa wątki, a następnie czeka na ich synchronizację przy użyciu klasy CyclicBarrier. Problem polega na tym, że cykliczna bariera nie działa zgodnie z oczekiwaniami, a główny wątek nie czeka na zakończenie poszczególnych wątków. Oto, jak wygląda mój kod:Jak uzyskać java.concurrency.CyclicBarrier do pracy zgodnie z oczekiwaniami

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

Każdy pomysł, co robię źle? Czy istnieje lepszy sposób na zapisanie tych metod synchronizacji barier? Proszę pomóż.

Odpowiedz

14

Podczas wykonywania twojej w wątku tworzysz dwa inne wątki i każ im czekać na siebie nawzajem. Ale nic nie napisałeś, aby główny wątek na nich czekał i narzekasz, że nie czeka. Wypróbuj

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

BTW. Nie rozszerzaj klasy wątków, chyba że musisz. Implementuj Runnable i przekazuj implementacje do obiektów Thread. Tak:

class MyRunnable implements Runnable { 
    public void run(){ 
     // code to be done in thread 
    } 
} 

Thread thread1 = new Thread(MyRunnable); 
thread1.start(); 

EDIT
Uzasadnienie dla uniknięcia rozciągający wątek.
Regułą jest jak najmniejsze sprzężenie. Dziedziczenie to bardzo silny związek między klasami. Musisz dziedziczyć z wątku, jeśli chcesz zmienić niektóre jego domyślne zachowanie (to znaczy zastąpić niektóre metody) lub chcesz uzyskać dostęp do niektórych chronionych pól wątku klasy. Jeśli nie chcesz tego, wybierz luźniejsze sprzężenie - implementacja Runnable i przekazanie go jako parametru konstruktora do instancji Thread.

+0

Jednak ten sprawia, że ​​jest synchroniczny, podczas gdy OP wydaje się chcieć prawdziwie asynchronicznego podejścia. –

+1

@Chandru: Cóż, albo "Należy poczekać, aż oba wątki zakończą wykonywanie przed wydrukowaniem tego" lub asynchronicznie. Nie możesz czekać asynchronicznie. –

+0

Jeśli rozumiem to poprawnie, to, czego potrzebuje, to wydrukowanie tej linii po ukończeniu 2 wątków, co można osiągnąć, robiąc to w akcji bariery. W ten sposób, jeśli w bieżącym wątku są inne operacje, które nie zależą od zakończenia pozostałych 2 wątków, mogą one kontynuować. BTW Nie byłem tym, który głosował na ciebie. Jest to nadal poprawne rozwiązanie, jeśli wszystkie inne operacje w bieżącym wątku zależą od ukończenia 2 wątków. –

2

Przekaż instancję Runnable do konstruktora swojego modelu CyclicBarrier w ten sposób.

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(); 
+0

Nadal nie czeka na zakończenie obu programów. Czy istnieje wyraźny sposób, jaki mogę powiedzieć, poczekać do zakończenia wątku1 i wątku2? –

+0

Tak, mogę. Thread.join() jest na to. – malaverdiere

+0

Jeśli chcesz naprawdę asynchronicznie czekać na zakończenie wątku, po co zawracać sobie głowę, jeśli metoda zakończy się przed jej zakończeniem? Ogólnie rzecz biorąc, chcesz zrobić coś, gdy oba wątki zakończą wykonywanie.Po prostu umieść to w callbacku na CyclicBarrier i jesteś gotowy do pracy. –

2

Szukasz Thread.join() metody ...

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

EDIT: z powodu komentarzach ...

A jeśli nie chcesz czekać w nieskończoność można określ także maksymalną liczbę milisekund plus nanosekund, aby poczekać na zgon wątku.

+0

Ale to jest trudne, ponieważ jeśli thread1.join() nie nastąpi, to thread2.join() będzie czekał w nieskończoność. Powodem, dla którego wybrałem CyclicBarrier, jest to, że wydaje się naprawdę asynchroniczny w oczekiwaniu. –

+1

@Ritesh, ale jeśli thread1.join nie stanie się, na co czekasz? jaki jest twój użytek? potrzebujesz implementacji "EITHER thread1 OR thread2 finished"? pgras pokazuje, jak zaimplementować "poczekaj aż oba skończą się" – Karussell

+1

@Ritesh, Albo chcesz poczekać na zakończenie zarówno 'thread1', jak i 'thread2' (w takim przypadku ta odpowiedź jest prawidłowa), lub nie (w takim przypadku wersja w pytaniu jest poprawna.) – finnw

1

Cykliczna bariera nie jest właściwym wyborem w tym przypadku. Musisz użyć tutaj CountDownLatch.

Zakładam, że wywołałeś metodę spawnAndWait z metody głównej.

Powodem tego nie jest to, że CyclicBarrier ma 2 konstruktorów. Aby wykonywać operacje po zakończeniu, musisz użyć konstruktora z dwoma parametrami. Najważniejszą rzeczą do zapamiętania jest to, że główny wątek nie będzie czekać przez metodę await; ale nadal będzie wykonywany. Jednak wątek określony w konstruktorze CyclicBarrier będzie działał tylko wtedy, gdy wszystkie zarodkowane wątki zatrzymają się przy barierce (metoda await).