Skip to content

Unexpected behaviour of Threads

I am trying to achieve that thread2 should complete first, then thread1, For this O am using join() method. But if I uncomment the System.out.println() present in the try block of thread1 class. then code give null pointer exception. Why in try block I need to add line, it doesn’t make any sense that adding a line code start working.

Demo class

public class Demo {

    public static void main(String[] args) throws InterruptedException {

        Thread1 t1 = new Thread1();
        Thread2 t2 = new Thread2();
        t1.start();
        t2.start();

        System.out.println("main Thread");
        Thread.sleep(10);
    }
}

Thread1 class

public class Thread1 extends Thread {
    @Override
    public void run() {
        try {
//            System.out.println(); // on adding anyline, this whole code works!!, uncommenting this line of code give NPE
            Thread2.fetcher.join();

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        for (int i = 0; i < 5; i++) {

            System.out.println("in thread1 class, Thread-1 ");
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

Thread2 class

public class Thread2 extends Thread {

    static Thread fetcher;

    @Override
    public void run() {

        fetcher= Thread.currentThread(); // got the thread2
        for (int i = 0; i < 5; i++) {
            System.out.println("in thread2 class, Thread-2");
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

}

OUTPUT of the program

in thread2 class Thread-2
Exception in thread "Thread-0" java.lang.NullPointerException
    at org.tryout.Thread1.run(Thread1.java:22)
in thread2 class Thread-2
in thread2 class Thread-2
in thread2 class Thread-2
in thread2 class Thread-2

Answer

It is working purely by “pure luck” the

System.out.println();

internally calls synchronized, which is working as a delay that gives enough time for Thread 2 its field fetcher in:

fetcher= Thread.currentThread(); // got the thread2

In order to avoid this race-condition you need to ensure that the Thread 2 sets the field fetcher before Thread 1 accesses it. For that you case use, among others, a CyclicBarrier.

??A synchronization aid that allows a set of threads to all wait for each other to reach a common barrier point.** CyclicBarriers are useful in programs involving a fixed sized party of threads that must occasionally wait for each other. The barrier is called cyclic because it can be re-used after the waiting threads are released.

First, create a barrier for the number of threads that will be calling it, namely 2 threads:

CyclicBarrier barrier = new CyclicBarrier(2);

With the CyclicBarrier you can then force Thread 1 to wait for Thread 2 before accessing its field fetcher:

    try {
        barrier.await(); // Let us wait for Thread 2.
        Thread2.fetcher.join();
    } catch (InterruptedException | BrokenBarrierException e) {
        // Do something 
    }

Thread 2 also calls the barrier after having setting up the field fetcher, accordingly:

    fetcher = Thread.currentThread(); // got the thread2
    try {
        barrier.await();
    } catch (InterruptedException | BrokenBarrierException e) {
        e.printStackTrace();
    }

Both threads will continue their work as soon as both have called the barrier.

An example:

public class Demo {

    public static void main(String[] args) throws InterruptedException             { 
        CyclicBarrier barrier = new CyclicBarrier(2);
        Thread1 t1 = new Thread1(barrier);
        Thread2 t2 = new Thread2(barrier);
        t1.start();
        t2.start();
        System.out.println("main Thread");
        Thread.sleep(10);
    }
}

public class Thread1 extends Thread {
    final CyclicBarrier barrier;

    public Thread1(CyclicBarrier barrier){
        this.barrier = barrier;
    }

    @Override
    public void run() {
        try {
            barrier.await();
            Thread2.fetcher.join();
        } catch (InterruptedException | BrokenBarrierException e) {
            // Do something 
        }
        for (int i = 0; i < 5; i++) {
            System.out.println("in thread1 class, Thread-1 ");
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class Thread2 extends Thread {
    static Thread fetcher;
    final CyclicBarrier barrier;

    public Thread2(CyclicBarrier barrier){
        this.barrier = barrier;
    }

    @Override
    public void run() {

        fetcher = Thread.currentThread(); // got the thread2
        try {
            barrier.await();
        } catch (InterruptedException | BrokenBarrierException e) {
            e.printStackTrace();
        }
        for (int i = 0; i < 5; i++) {
            System.out.println("in thread2 class, Thread-2");
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

If your code is not for education purposes, and you are not force to use any particular synchronization mechanism for learning purposes. In the current context you can simply pass the thread 2 as parameter of the thread 1, and call join directly on it as follows:

public class Demo {
    public static void main(String[] args) throws InterruptedException {
        Thread2 t2 = new Thread2();
        Thread1 t1 = new Thread1(t2);
        t1.start();
        t2.start();
        System.out.println("main Thread");
        Thread.sleep(10);
    }
}

public class Thread1 extends Thread {
    final Thread thread2;

    public Thread1(Thread thread2){
        this.thread2 = thread2;
    }

    @Override
    public void run() {
        try {
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        for (int i = 0; i < 5; i++) {
            System.out.println("in thread1 class, Thread-1 ");
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class Thread2 extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println("in thread2 class, Thread-2");
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}