Home > Back-end >  Java wait() notify() methods last thread waiting forever
Java wait() notify() methods last thread waiting forever

Time:05-25

I were playing with Java synchronization mechanism and faced the situation I can not explain.

In the code sample below I have a simple application which is spawning some threads, which are waiting a signal from an object and notify each other once they have been notified:

public class Main {
    public static final Object o = new Object();

    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 6;   i) {
            new Thread(() -> {
                try {
                    synchronized (o) {
                        o.wait();
                        System.out.println("Some work");
                        o.notify();
                    }
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println(Thread.currentThread().getName()   " notified someone");
            }).start();
        }
        synchronized (o) {
            o.notify();
        }
    }
}

And most of the times application's last created thread is hanging: The response is:

Some work
Some work
Some work
Some work
Some work
Thread-3 notified someone
Thread-4 notified someone
Thread-0 notified someone
Thread-2 notified someone
Thread-1 notified someone

After applying trial and error technique I found out that if I put a pause Thread.sleep(100); between loop and notification in main thread app starts working fine.

After investigation I found out that threads state right after notification sent from main method is:

main "Thread-0" prio=5 Id=13 RUNNABLE
main "Thread-1" prio=5 Id=14 WAITING on java.lang.Object@2133c8f8 owned by "Thread-0" Id=13
main "Thread-2" prio=5 Id=15 WAITING on java.lang.Object@2133c8f8 owned by "Thread-0" Id=13
main "Thread-3" prio=5 Id=16 WAITING on java.lang.Object@2133c8f8 owned by "Thread-0" Id=13
main "Thread-4" prio=5 Id=17 WAITING on java.lang.Object@2133c8f8 owned by "Thread-0" Id=13
main "Thread-5" prio=5 Id=18 BLOCKED on java.lang.Object@2133c8f8 owned by "Thread-0" Id=13

And right before the last notification is sent the state is:

Thread-4 "Thread-4" prio=5 Id=17 RUNNABLE
Thread-4 "Thread-5" prio=5 Id=18 BLOCKED on java.lang.Object@2133c8f8 owned by "Thread-4" Id=17

// Final state

"Thread-5" prio=5 Id=18 WAITING on java.lang.Object@2133c8f8

So that is obvious that the last thread has been blocked from the beginning of the app and unable to receive the notification from the penultimate thread, finally reached critical section code and staying on wait line awaiting for the notification.

Probably it's blocked initially because of there's a clash with main method when they both trying to get the o lock.

It would be nice if someone who much better knows this multithreading stuff give some explanations or just some hints of this behavior to me.

CodePudding user response:

The problem is in calling notify when no other thread is waiting. Semaphores and job queues highlight what you are missing

Semaphore

public class Semaphore {
  private int tickets;
  public Semaphore (int tickets) {
    this.tickets = ticket;
  }

  public synchronized void getTicket() {
    if (tickets > 0) {
      tickets--;
    } else {
      while (tickets == 0) {
        wait();
      }
      tickets--;
    }
  }

  public synchronized void releaseTicket() {
    tickets  ;
    notify();
  }
}

A job queue works similarly, but instead of tickets, it has a queue of Runnables. When a thread calls getJob, if there's a job on the queue, it dequeues the job and hands it to the thread. Otherwise, the thread waits for a job.

CodePudding user response:

You're noticing the distinction between a thread waiting to enter a synchonized block ( BLOCKED ) and a thread blocked on a wait() call ( WAITING.)

You have a race condition in your loop.You create your threads and start them. Most of them will make it to the synchronized block. Each of those threads gets to a synchronize block, grabs the lock, and releases it on the wait.

The last thread doesn't have time to run to the synchonized block before your main thread enters the synchronized block. Once your main has acquired the lock on your object, notifies a Thread waiting on the lock. That thread wakes up and acquires the lock, without letting the last thread into the synchronized block.

The first five threads finish and notify, and one thread notifies nobody. Then the last thread has a chance to enter the synchronized block and call wait, but it will never get notified.

That is why the Thread.sleep helps, because your last thread will have time to reach the synchronized block and wait.

  • Related