2013-05-24 3 views
5

Я написал простой пример для потоковой передачи, который генерирует таблицы для чисел, начиная с 1 до 20. Когда я тестировал его с помощью основного метода, он выполняет все потоки (печатает все сообщения), в то время как все потоки не выполняются (все сообщения не печатаются) большую часть времени (иногда он запускает все потоки), когда они делают то же самое с тестом JUnit. Я думаю, что не должно быть никакой разницы в объемах выпуска.JUnit test не выполняет все потоки, созданные в тесте

Вот класс с основным методом:

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

    } 

} 

Когда я вызываю основной метод, который он печатает все результаты.

пыльник код для тестового JUnit, эквивалентной основной метод:

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

} 

При запуске выше тестовый пример, поведение на выходе не последовательны в сцене, что иногда она печатает все сообщения и большую часть времени печатает только несколько и выходы. Вот вывод захвачен в вышеуказанном случае тест 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 

Выхода здесь заканчивается без печати оставшихся сообщений в других потоках/выполнения других потоков.

Может кто-нибудь помочь мне понять причину этого. Заранее спасибо.

ответ

11

JUnit выходит из метода тестирования на ранней стадии. Вам нужно дождаться завершения всех потоков, прежде чем вы выйдете из метода testCalculator().

Простой способ сделать это с помощью CountDownLatch.

  1. Инициализировать CountDownLatch с CountDownLatch latch = new CountDownLatch(20).

  2. Пропускать каждый Calculator с возможностью ссылки на защелку. В конце метода run() вызовите latch.countDown().

  3. В конце testCalculator() метод вызова latch.await(). Это будет заблокировано до тех пор, пока latch.countDown() не будет вызван 20 раз (то есть когда все потоки завершены).

+0

Спасибо за ответ. Я попробую. –

+0

ОК. Я получил ваше мнение, но все же я не могу понять, почему основной поток ждет, чтобы все потоки, запущенные им, завершились/завершились, в то время как тестовый поток JUnit не разрешил это. Не могли бы вы объяснить это. –

+0

Вы спрашиваете, как работает счетчик обратного отсчета? Или вы спрашиваете, почему вам нужно блокировать до тех пор, пока все потоки не будут завершены? –

4

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

Если вы хотите запустить такой тест, вы должны сохранить коллекцию созданных вами потоков и join() каждого из них в конце вашего тестового метода. Вызовы join() каждый поток выполняется во втором цикле (после цикла, который запускает все потоки).

+1

Это не будет использовать параллелизм, хотя ... только один поток будет запущен сразу. Есть способы обойти это, используя вместо этого метод 'CountDownLatch' или' ExecutorService # invokeAll (List ) '. –

+1

Все потоки имеют 'start()' вызываемый в существующем цикле. Ответ заключается в том, чтобы добавить второй цикл в конце тестового метода, который вызывает 'join()' на каждом порожденном потоке. Вызов 'join()' не возвращается, пока этот поток не завершится.В полной мере использует параллелизм и гарантирует, что все порожденные потоки будут выполнены до возвращения. CountDownLatch требует тщательного кодирования для обеспечения того, чтобы каждый поток учитывался, даже если он имеет исключения. ExecutorService все еще требует, чтобы вы подождали, пока не будет выполнена Callable (или Runnable). – Rob

+0

Ahh, ОК. Я соглашаюсь использовать 'join()' then. Возможно, вы захотите прояснить «второй цикл в конце метода» в своем ответе. Использование 'CountDownLatch' будет работать отлично для OP, хотя (никакие исключения не выбрасываются в runtime). И «ExecutorService» может быть лучшим вариантом здесь, потому что вместо того, чтобы создавать 20 потоков независимо от того, на каком компьютере вы работаете, вы можете создать пул потоков, используя вместо них «Runtime.getRuntime()). AccessProcessors()' threads. Затем вызов 'invokeAll()' будет выполнять все вызовы и будет блокироваться до тех пор, пока они не будут завершены. –

Смежные вопросы