Home > database >  Java Thread is blocked in join()
Java Thread is blocked in join()

Time:11-04

I have the following simple code in which I put and take from a Queue represented as an ArrayList.

public class EmailService {

    private Queue<Email> emailQueue;
    private Object lock;
    private volatile boolean run;
    private Thread thread;

    public void sendNotificationEmail(Email email) throws InterruptedException {
        emailQueue.add(email);

        synchronized (lock) {
            lock.notify();
            lock.wait();
        }
    }

    public EmailService() {
        lock = new Object();
        emailQueue = new Queue<>();
        run = true;
        thread = new Thread(new Runnable() {
            @Override
            public void run() {
                while (run) {
                    System.out.println("ruuuning");
                    synchronized (lock) {
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        if (emailQueue.getSize() > 0) {
                            sendEmail(emailQueue.poll());
                        }
                        lock.notify();
                    }
                }
            }
            private void sendEmail(Email email) {
                System.out.println("Sent email from "   email.getFrom()   " to "   email.getTo()   " with content: "   email.getContent());
            }
        });
        thread.start();
    }

    public void close() throws InterruptedException {
        run = false;
        synchronized (lock) {
            lock.notify();
            System.out.println("Thread will join "   thread.isInterrupted());
            thread.join();
            System.out.println("Thread after join");
        }
    }
}

I don't understand why my thread is blocked in join() method. From main I call as follow:

eService = new EmailService();
Email e1 = new Email(client1, client2, "content1");
eService.sendNotificationEmail(e1);
eService.close();

CodePudding user response:

Without running it...

  • The close() method holds lock at the time it calls thread.join() and waits on thread (forever)
  • thread is waiting to reacquire lock so cannot run

Both are now waiting on each other, this is a deadlock. Try moving the Thread.join() after the synchronized block:

    public void close() throws InterruptedException {
        run = false;
        synchronized (lock) {
            lock.notify();
            System.out.println("Thread will join "   thread.isInterrupted());
        }
        thread.join();
        System.out.println("Thread after join");
    }

CodePudding user response:

@drekbour explained how your program could hang in the join() call, but FYI: Here's a different way that your program could hang. This is called lost notification.

Your main thread creates a new EmailService instance. The new instance creates its thread and calls thread.start() *BUT* it could take some time for the thread to actually start running. Meanwhile...

Your main thread creates a new Email instance, and calls eService.sendNotificationEmail(...). That function adds the new message to the queue, locks the lock, notifies the lock, and then waits on the lock.

Finally, The service thread starts up, enters its run() method, locks the lock, and then it calls lock.wait().

At this point, the program will be stuck because each thread is waiting to be notified by the other.


The way to avoid lost notification is, in the consumer thread, do not call wait() if the thing that you are waiting for already has happened.

synchronized(lock) {
    while (theThingHasNotHappenedYet()) {
        lock.wait();
    }
    dealWithTheThing();
}

In the producer thread:

    synchronized(lock) {
        makeTheThingHappen();
        lock.notify();
    }

Notice how both threads lock the lock. Ever wonder why lock.wait() throws an exception if the lock isn't locked? The examples above illustrate why. The lock prevents the producer thread from making the thing happen after the consumer already has decided to wait. That is key: If the consumer were to wait after the producer calls notify() then it's game over. The program hangs.

  • Related