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) {
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.
source share