Search code examples
javajava-threads

Threads stopped print characters and all of them are waiting for lock


I want to use 3 threads to print ABC randomly,so I wrote code as below:

public class ThreadPrint4Test {

    public static void main(String[] args) {
        new ThreadPrint4Test().testPrint();
    }

    public void testPrint() {
        Object lock = new Object();
        new Thread(new PrintThread("A",lock),"thread-A").start();
        new Thread(new PrintThread("B",lock),"thread-B").start();
        new Thread(new PrintThread("C",lock),"thread-C").start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        new Thread(() -> {
            synchronized (lock) {
                lock.notifyAll();
            }
        }).start();
    }

    class PrintThread implements Runnable {

        private Object lock;
        private String value;

        public PrintThread(String value, Object lock) {
            this.value = value;
            this.lock = lock;
        }

        public void run() {
            while (true) {
                try {
                    synchronized (lock) {
                        lock.wait();
                        System.out.println(LocalTime.now() + "\t" + value);
                        lock.notifyAll();
                    }
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }
}

But when I test the program,it stoppted print characters after a few seconds enter image description here

Using jps and jstack I found all the 3 threads are WAITING the object lock enter image description here

I tested it for serveral times,and this phenomenon occurs every time.

I do not know why all the 3 threads are waiting object lock at same time

Based on my knowledge,each time one of the 3 threads will be awake and have the chance to exeute,then it will awake all the 3 threads randomly by invoking lock.notifyAll();. So the program should run continuous theoretically,But now each time I test it,it will stop print characters after a few seconds.

Could someone help analysis why this happen? Thanks in advance!

        while (true) {
            try {
                synchronized (lock) {
                    lock.wait();
                    System.out.println(LocalTime.now() + "\t" + value);
                    lock.notifyAll();
                }
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }

Solution

  • Your problem is that you are calling notifyAll() immediately after calling wait(). You get to a deadlock situation where each thread is waiting. When all the threads are waiting, none of them can be notified, hence your program comes to a standstill.

    According to the Concurrency tutorial, you need to wait() for a certain condition to occur. Once the condition occurs, you perform the required action (which, in your code, is printing the current time) then you call notifyAll() to inform the waiting threads that you have completed performing the required action and that they can try to perform their required action.

    Explanations after the code.

    import java.time.LocalTime;
    
    public class ThreadPrint4Test {
        private volatile boolean canPrint;
    
        public void testPrint() {
            Object lock = new Object();
            new Thread(new PrintThread("A", lock), "thread-A").start();
            new Thread(new PrintThread("B", lock), "thread-B").start();
            new Thread(new PrintThread("C", lock), "thread-C").start();
        }
    
        class PrintThread implements Runnable {
            private Object lock;
            private String value;
    
            public PrintThread(String value, Object lock) {
                this.value = value;
                this.lock = lock;
                canPrint = true;
            }
    
            public void run() {
                while (true) {
                    while (!canPrint) {
                        try {
                            synchronized (lock) {
                                System.out.printf("[%s] Waiting...%n", value);
                                lock.wait();
                            }
                        }
                        catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    }
                    synchronized (lock) {
                        canPrint = false;
                        System.out.println(LocalTime.now() + "\t" + value);
                        canPrint = true;
                        lock.notifyAll();
                    }
                }
            }
        }
        public static void main(String[] args) {
            new ThreadPrint4Test().testPrint();
            try {
                Thread.sleep(2000);
            }
            catch (InterruptedException x) {
                x.printStackTrace();
            }
            System.exit(0);
        }
    }
    
    • I added a canPrint flag. When it is true, then the thread can print the current time. When it is false, the thread must wait.
    • When the thread can print the current time, it first sets the flag to false so that no other thread can print.
    • After the thread has printed the current time, it sets the flag back to true and calls method notifyAll() which causes all waiting threads to stop waiting and re-check the canPrint flag.
    • The first thread to awake after canPrint has been set to true, sets the flag to false which causes all other threads to wait.