8

I wrote a simple example for threading which generates tables for numbers starting from 1 to 20. when I tested it with main method it executes all the threads (prints all the messages), while all threads are not being run (all messages are not being printed) most of the times (sometimes it runs all the threads) when doing the same with JUnit test. I think there should not be any difference in terms of output.

Here is the class with main method:

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

     }

}

When I invoke the main method it prints all the results.

Bellow is the code for JUnit test equivalent to the main method:

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

}

When I run the above test case, the behavior of the output is not consistant in the scene that sometimes it prints all the messages and most of the times prints only a few and exits. Here is the output captured in case of the above JUnit test case:

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 

Output ends here without printing the remaining messages in other threads/ executing other threads.

Can somebody help me to understand the reason behind this. Thanks in advance.

Vaibhav Raj
  • 2,214
  • 4
  • 23
  • 39

2 Answers2

13

JUnit is exiting the test method early. You need to wait for all of the threads to complete before you exit the testCalculator() method.

An easy way to do that is by using a CountDownLatch.

  1. Initialize a CountDownLatch with CountDownLatch latch = new CountDownLatch(20).

  2. Pass each Calculator runnable a reference to the latch. At the end of the run() method, call latch.countDown().

  3. At the end of the testCalculator() method call latch.await(). This will block until latch.countDown() has been called 20 times (i.e. when all threads have completed).

Alex Lockwood
  • 83,063
  • 39
  • 206
  • 250
  • OK. I got your point but still I am not able to understand why the main thread waits for all the threads started by it to complete/finish, while at the same time the JUnit test thread is not allowing this. Can you please explain it. – Vaibhav Raj May 24 '13 at 17:50
  • Are you asking how the count down latch works? Or are you asking why do you need to block until all of the threads have completed? – Alex Lockwood May 24 '13 at 19:05
  • 1
    Not about count down latches, but about the difference between main thread and JUnit's thread in which the test method is running. Why both are behaving differently? – Vaibhav Raj May 25 '13 at 01:50
  • 5
    JUnit is a unit testing framework... as with the Android framework, it has a main thread from which it will call user-defined unit test methods. When a unit test returns, JUnit immediately invokes the next unit test method (or exits completely if there are no more unit test methods to call). JUnit doesn't know anything about the background threads you create/start, so you can't assume that it will sit around and wait for them to finish. Does that make sense? – Alex Lockwood May 25 '13 at 02:33
5

Your test method finishes before all the spawned threads are finished. When the JUnit executor finishes, all spawned threads are killed.

If you want to run this kind of test, you should keep a collection of the threads you have created and join() each of them at the end of your test method. The calls to join() each thread are executed in a second loop (following the loop that starts all the threads).

Something like this:

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

If you want to have the threads all start around the same time (e.g., if your loop that is creating the threads does some non-trivial work each time through the loop):

@Test
public void testCalculator() {
    List<Thread> threads = new ArrayList<>();
    for (int i = 1; i < 21; i++) {
        threads.add(new Thread(new Calculator(i)));
    }
    for (Thread thread : threads) {
        thread.start();
    }
    for (Thread thread : threads) {
        thread.join();
    }
}
Rob
  • 6,247
  • 2
  • 25
  • 33
  • 1
    This won't take advantage of parallelism though... only one thread will ever be running at once. There are ways around this by using a `CountDownLatch` or the `ExecutorService#invokeAll(List)` method instead. – Alex Lockwood May 24 '13 at 16:21
  • 1
    All threads have `start()` called in the existing loop. The answer is to add a second loop at the end of the test method that calls `join()` on each spawned thread. The `join()` call doesn't return until that thread has finished. Takes full advantage of the parallelism and ensures that all spawned threads are done before returning. CountDownLatch requires careful coding to ensure that every threads counts down even if it has exceptions. ExecutorService still requires that you wait until the Callable (or Runnable) is done. – Rob May 24 '13 at 16:25
  • Ahh, OK. I agree about using `join()` then. You might want to clarify the "second loop at the end of the method" in your answer. Using a `CountDownLatch` would work fine for the OP though (no exceptions are thrown in the `Calculator` runnable). And the `ExecutorService` might even be the best option here because instead of creating 20 threads no matter what computer you are executing on, you can create a thread pool using `Runtime.getRuntime().availableProcessors()` threads instead. Then calling `invokeAll()` will execute all of the callables and will block until they have all completed. – Alex Lockwood May 24 '13 at 16:33
  • I tried your approach. It's working fine. But I want to know the reason behind why in case of main method threads are alive and running and in case of JUnit method it's not happening like in main. – Vaibhav Raj May 24 '13 at 17:56
  • In the `main()` case, the JVM doesn't stop until all the non-deamon threads finish - the threads keep the JVM alive until they complete. If you call `setDaemon(true)` on your threads, you will see the `main()` case behave like junit. The JUnit framework runs the test cases and then calls `System.exit()` which exits the JVM immediately, killing all threads. By explicitly waiting for the threads to finish using `join()`, you are essentially letting JUnit know that your test is not done until those threads finish (because your test case doesn't finish until the threads are done). – Rob May 25 '13 at 03:11