Getting thread to pause - Thread.wait () / Thread.notify ()

I am trying to understand how streams work, and I wrote a simple example where I want to create and start a new stream, a stream, display numbers from 1 to 1000 in the main stream, resume the secondary stream, and display numbers from 1 to 1000 in the secondary stream. When I ignore Thread.wait () / Thread.notify (), it behaves as expected, both threads display multiple numbers at a time. When I add these features, for some reason, the main thread numbers are printed second rather than first. What am I doing wrong?

public class Main { public class ExampleThread extends Thread { public ExampleThread() { System.out.println("ExampleThread name is: " + this.getName()); } @Override public void run() { for(int i = 1; i < 1000; i++) { System.out.println(Thread.currentThread().getName()); System.out.println(i); } } } public static void main(String[] args) { new Main().go(); } public void go() { Thread t = new ExampleThread(); t.start(); synchronized(t) { try { t.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } for(int i = 1; i < 1000; i++) { System.out.println(Thread.currentThread().getName()); System.out.println(i); } synchronized(t) { t.notify(); } } } 
+4
source share
3 answers

You do not understand how wait / notify . wait does not block the thread on which it is called; it blocks the current thread until notify is called on the same object (so if you have threads A and B, and in thread A - B.wait () this will stop thread A and not thread B - until B.notify () will not be called).

So, in your specific example, if you want the main thread to be executed first, you need to put wait () in the secondary thread. Like this:

 public class Main { public class ExampleThread extends Thread { public ExampleThread() { System.out.println("ExampleThread name is: " + this.getName()); } @Override public void run() { synchronized (this) { try { wait(); } catch (InterruptedException e) { } } for(int i = 1; i < 1000; i++) { System.out.println(Thread.currentThread().getName()); System.out.println(i); } } } public static void main(String[] args) { new Main().go(); } public void go() { Thread t = new ExampleThread(); t.start(); for(int i = 1; i < 1000; i++) { System.out.println(Thread.currentThread().getName()); System.out.println(i); } synchronized(t) { t.notify(); } } } 

However, even this code may not work the way you want. In a scenario where the main thread falls into the notify () part, the secondary thread had the opportunity to get to the wait () part (unlikely in your case, but still possible - you can watch it if you place Thread.sleep at the beginning of the secondary thread ), the secondary thread will never wake up. Therefore, the safest way would be something like this:

 public class Main { public class ExampleThread extends Thread { public ExampleThread() { System.out.println("ExampleThread name is: " + this.getName()); } @Override public void run() { synchronized (this) { try { notify(); wait(); } catch (InterruptedException e) { } } for(int i = 1; i < 1000; i++) { System.out.println(Thread.currentThread().getName()); System.out.println(i); } } } public static void main(String[] args) { new Main().go(); } public void go() { Thread t = new ExampleThread(); synchronized (t) { t.start(); try { t.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } for(int i = 1; i < 1000; i++) { System.out.println(Thread.currentThread().getName()); System.out.println(i); } synchronized(t) { t.notify(); } } } 

In this example, the output is completely deterministic. Here's what happens:

  • The main thread creates a new object t .
  • The main thread gets a lock on monitor t .
  • The main thread starts thread t .
  • (this can happen in any order)
    • The secondary thread starts, but since the main thread still belongs to monitor t , the secondary thread cannot continue and must wait (because its first statement is synchronized (this) , not because it happens with the be object t - all locks, notifications, and expectations can also be fulfilled on an object that is not fully associated with either of the two threads with the same result.
    • The primary stream continues, goes to the t.wait() and pauses its execution, freeing up the monitor t to which it is synchronized.
  • The secondary thread gains ownership t .
  • The secondary thread calls t.notify() , waking up the main thread. The main thread cannot yet continue, although the secondary thread still remains the owner of the t monitor.
  • Secondary call flows t.wait() , pauses its execution and frees up t monitor.
  • The primary thread may finally continue, since t monitor is now available.
  • The primary thread gains ownership of the t monitor, but immediately releases it.
  • The primary thread counts the number.
  • The primary thread gains ownership of the monitor t again.
  • The primary chain calls t.notify() , waking up the secondary stream. The secondary stream cannot continue yet, because the main stream still contains monitor t .
  • The primary thread releases monitor t and exits.
  • The secondary thread gains ownership of monitor t , but immediately releases it.
  • The secondary thread counts the number and then ends.
  • The entire application ends.

As you can see, even in such a deceptively simple scenario, much happens.

+11
source

ExampleThread not wait() or notify() , and not synchronized on anything. Therefore, it will be launched whenever possible, without any coordination with other threads.

The main thread is waiting for a notification that never arrives (this notification must be sent by another thread). I assume that when ExampleThread dies, the main thread wakes up "falsely" and ends.

A thread that must wait for another to complete must make a wait() call inside the loop, which checks the condition:

 class ExampleThread extends Thread { private boolean ready = false; synchronized void ready() { ready = true; notifyAll(); } @Override public void run() { /* Wait to for readiness to be signaled. */ synchronized (this) { while (!ready) try { wait(); } catch(InterruptedException ex) { ex.printStackTrace(); return; /* Interruption means abort. */ } } /* Now do your work. */ ... 

Then in your main thread:

 ExampleThread t = new ExampleThread(); t.start(); /* Do your work. */ ... /* Then signal the other thread. */ t.ready(); 
+2
source

You are fortunate that your program generally ends.

When you call t.wait() , your main threads stop and indefinitely wait for notification.

He never receives it, but I am sure that he is awakened by a false awakening when the secondary stream ends. (Read here that fake awakening).

+2
source

Source: https://habr.com/ru/post/1392576/


All Articles