2013-05-24 10 views
5

Napisałem prosty przykład wątkowania, który generuje tabele dla liczb zaczynając od 1 do 20. Kiedy testowałem to z główną metodą, wykonuje wszystkie wątki (drukuje wszystkie wiadomości), podczas gdy wszystkie wątki nie są uruchamiane (wszystkie wiadomości nie są drukowane) przez większość czasu (czasami uruchamia wszystkie wątki), gdy robią to samo z testem JUnit. Myślę, że nie powinno być żadnych różnic pod względem wydajności.JUnit test nie wykonuje wszystkich wątków utworzonych w ramach testu

Oto klasa metodą głównej:

public class Calculator implements Runnable { 

    private int number; 

    Calculator(final int number){ 
     this.number = number; 
    } 

    @Override 
    public void run() { 
     for(int i = 1; i <= 10; i++){ 
      System.out.printf("%s : %d * %d = %d \n", Thread.currentThread().getName(), number, i, number * i); 
     } 

    } 

    public static void main(String[] args){ 
     Calculator calculator = null; 
     Thread thread = null; 
     for(int i = 1; i < 21; i ++){ 
      calculator = new Calculator(i); 
      thread = new Thread(calculator); 
      System.out.println(thread.getName() + " Created"); 
      thread.start(); 
      System.out.println(thread.getName() + " Started"); 
     } 

    } 

} 

Kiedy wywołać główne metody to drukuje wszystkie wyniki.

Bellow jest kod na ekwiwalent testu JUnit do głównej metody:

public class CalculatorTest { 

private Calculator calculator; 
private Thread thread; 

@Test 
public void testCalculator() { 
    for(int i = 1; i < 21; i ++){ 
     calculator = new Calculator(i); 
     thread = new Thread(calculator); 
     System.out.println(thread.getName() + " Created"); 
     thread.start(); 
     System.out.println(thread.getName() + " Started"); 
    } 
} 

} 

Kiedy uruchomić powyższy przypadek testowy, zachowanie produkcji nie jest spójne w scenie, że czasami drukuje wszystkie wiadomości a większość razy drukuje tylko kilka i wychodzi. Oto wynik ujęte w przypadku powyższego testu JUnit:

Thread-0 Created 
Thread-0 Started 
Thread-1 Created 
Thread-1 Started 
Thread-2 Created 
Thread-2 Started 
Thread-3 Created 
Thread-3 Started 
Thread-4 Created 
Thread-4 Started 
Thread-5 Created 
Thread-5 Started 
Thread-6 Created 
Thread-6 Started 
Thread-7 Created 
Thread-7 Started 
Thread-8 Created 
Thread-8 Started 
Thread-9 Created 
Thread-9 Started 
Thread-10 Created 
Thread-10 Started 
Thread-11 Created 
Thread-11 Started 
Thread-12 Created 
Thread-12 Started 
Thread-13 Created 
Thread-13 Started 
Thread-14 Created 
Thread-14 Started 
Thread-15 Created 
Thread-15 Started 
Thread-16 Created 
Thread-16 Started 
Thread-17 Created 
Thread-17 Started 
Thread-18 Created 
Thread-18 Started 
Thread-19 Created 
Thread-19 Started 
Thread-0 : 1 * 1 = 1 
Thread-0 : 1 * 2 = 2 
Thread-0 : 1 * 3 = 3 
Thread-0 : 1 * 4 = 4 
Thread-0 : 1 * 5 = 5 
Thread-0 : 1 * 6 = 6 
Thread-0 : 1 * 7 = 7 
Thread-0 : 1 * 8 = 8 
Thread-0 : 1 * 9 = 9 
Thread-0 : 1 * 10 = 10 
Thread-2 : 3 * 1 = 3 
Thread-2 : 3 * 2 = 6 
Thread-2 : 3 * 3 = 9 
Thread-2 : 3 * 4 = 12 
Thread-2 : 3 * 5 = 15 
Thread-2 : 3 * 6 = 18 
Thread-2 : 3 * 7 = 21 

Wyjście kończy się tutaj bez drukowania pozostałe wiadomości w innych wątków/wykonywanie innych wątków.

Czy ktoś może mi pomóc zrozumieć przyczynę tego. Z góry dziękuję.

Odpowiedz

11

JUnit wychodzi z metody testowej wcześniej. Przed zakończeniem metody testCalculator() należy poczekać na zakończenie wszystkich wątków.

Łatwym sposobem na to jest użycie CountDownLatch.

  1. zainicjować CountDownLatch z CountDownLatch latch = new CountDownLatch(20).

  2. Przepisz każde Calculator możliwe do uruchomienia odniesienie do zatrzasku. Pod koniec metody run(), zadzwoń pod numer latch.countDown().

  3. Pod koniec wywołania metody latch.await(). To zablokuje, aż latch.countDown() zostanie wywołane 20 razy (tj. Po zakończeniu wszystkich wątków).

+0

Dzięki za odpowiedź. Spróbuję. –

+0

OK. Dostałem twój punkt, ale nadal nie jestem w stanie zrozumieć, dlaczego wątek główny czeka na wszystkie wątki rozpoczęte przez niego, aby zakończyć/zakończyć, podczas gdy w tym samym czasie wątek testowy JUnit tego nie dopuszcza. Czy możesz to wyjaśnić? –

+0

Pytasz, jak działa blokada odliczania? A może pytasz, dlaczego musisz blokować, aż wszystkie wątki zostaną ukończone? –

4

Twoja metoda testowa kończy się, zanim wszystkie zarodkowane wątki zostaną ukończone. Po zakończeniu wykonywania JUnit, wszystkie zarodkowane wątki zostają zabite.

Jeśli chcesz uruchomić ten rodzaj testu, powinieneś zachować kolekcję utworzonych wątków i join() każdą z nich na końcu metody testowej. Wywołania do join() każdego wątku są wykonywane w drugiej pętli (po pętli, która uruchamia wszystkie wątki).

+1

Nie użyje to jednak paralelizmu ... tylko jeden wątek będzie kiedykolwiek uruchomiony na raz. Istnieją sposoby obejścia tego problemu za pomocą metody 'CountDownLatch' lub' ExecutorService # invokeAll (List ) '. –

+1

Wszystkie wątki mają 'start()' wywołane w istniejącej pętli. Odpowiedź brzmi: dodaj drugą pętlę na końcu metody testowej, która wywołuje 'join()' na każdym z wątku. Wywołanie "join()" nie powraca, dopóki ten wątek nie zostanie zakończony.Wykorzystuje w pełni paralelność i zapewnia, że ​​wszystkie zarodkowane wątki są wykonywane przed powrotem. CountDownLatch wymaga starannego kodowania, aby zapewnić, że każdy wątek odlicza, nawet jeśli ma wyjątki. Usługa ExecutorService nadal wymaga, abyś poczekał, aż zrobi się Callable (lub Runnable). – Rob

+0

Ahh, OK. Zgadzam się na używanie 'join()'. W odpowiedzi możesz wyjaśnić "drugą pętlę na końcu metody". Używanie 'CountDownLatch' będzie działało dobrze dla OP (nie ma żadnych wyjątków w' 'Kalkulatorze ''). A opcja 'ExecutorService' może być nawet najlepszą opcją, ponieważ zamiast tworzyć 20 wątków bez względu na komputer, na którym wykonujesz zadanie, możesz utworzyć pulę wątków za pomocą wątków' Runtime.getRuntime(). AvailableProcessors() '. Następnie wywołanie 'invokeAll()' wykona wszystkie podpalacze i zablokuje, aż wszystkie zostaną zakończone. –

Powiązane problemy