Status of the thread
Six thread states provided in the official Oracle documentation
Status nameexplainNEWIn the initial state, the thread is created, but the start() method has not been called, and the thread has not been startedRUNNABLEIn the running state, a thread starts to be executed in the java virtual machineBLOCKEDIn the blocking state, the thread is locked waiting to obtain the monitor lock of the object, in other words, it is blocked by the synchronizationWAITINGWait state, which is the state of a thread that waits indefinitely for another thread to perform a specific operation.TIMED_WAITINGTimeout waiting state, which is the state of a thread waiting for another thread to perform an operation within the specified waiting time.TERMINATEDThe thread has finished executing and exitedRealize inter thread cooperation
1, Wait methodThe JDK provides three versions of methods:
- The wait() method is used to suspend the currently running thread (that is, let it enter the blocking state) until the notify or notifyAll method wakes up the thread
- wait(long timeout). This method is similar to the wait() method. The only difference is that if there is no wake-up of notify or notifAll method within the specified time, it will wake up automatically.
(3) As for wait(long timeout,long nanos), it is intended to control the scheduling time more accurately. However, from the current version, it seems that this method does not fully realize this function. Its source code (JDK1.8) is as follows:
public final void wait(long timeout, int nanos) throws InterruptedException { if (timeout < 0) { throw new IllegalArgumentException("timeout value is negative"); } if (nanos < 0 || nanos > 999999) { throw new IllegalArgumentException( "nanosecond timeout value out of range"); } if (nanos >= 500000 || (nanos != 0 && timeout == 0)) { timeout++; } wait(timeout); }
From the source code, the processing of nanoseconds in JDK8 is only rounded, so it is still processed in milliseconds. Nanosecond accuracy may be used at some point in the future. Although the JDK provides these three versions, they are actually implemented by calling the wait(long timeout) method. The wait() method is equivalent to wait(0), and the wait(long timeout,int nanos) is also completed through wait(long timeout) from the above source code.
Let's use a simple example to demonstrate the use of the wait() method:
package com.paddx.test.concurrent; public class WaitTest { public void testWait(){ System.out.println("Start-----"); try { wait(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("End-------"); } public static void main(String[] args) { final WaitTest test = new WaitTest(); new Thread(new Runnable() { @Override public void run() { test.testWait(); } }).start(); } }
The intention of this code is very simple. After the program is executed, let it pause for one second and then execute it again.
View results:
Start----- Exception in thread "Thread-0" java.lang.IllegalMonitorStateException at java.lang.Object.wait(Native Method) at com.paddx.test.concurrent.WaitTest.testWait(WaitTest.java:8) at com.paddx.test.concurrent.WaitTest$1.run(WaitTest.java:20) at java.lang.Thread.run(Thread.java:745)
This program did not output the corresponding results as expected, but threw an exception. You may wonder why exceptions are thrown? What is the IllegalMonitorStateException thrown? We can take a look at the description of IllegalMonitorStateException in JDK:
Thrown to indicate that a thread has attempted to wait on an object 's monitor or to notify other threads waiting on an object' s monitor without owning the specified monitor.
The thread attempts to wait for the object monitor or to notify other threads waiting for the object monitor, but it does not own the corresponding monitor.
The wait method is a local method, and its bottom layer is completed through an object called monitor lock. Therefore, the reason why the above exception is thrown is that the ownership of the monitor object is not obtained when calling the wait method. How to obtain the ownership of the monitor object? In Java, you can only use the Synchronized keyword. Modify the above code and add the Synchronized keyword:
package com.paddx.test.concurrent; public class WaitTest { public synchronized void testWait(){//Add Synchronized keyword System.out.println("Start-----"); try { wait(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("End-------"); } public static void main(String[] args) { final WaitTest test = new WaitTest(); new Thread(new Runnable() { @Override public void run() { test.testWait(); } }).start(); } }
Now run the above code again, and you can see the expected effect:
Start----- End-------
Therefore, through this example, it should be clear that the use of the wait method must be within the scope of synchronization, otherwise an IllegalMonitorStateException will be thrown. The function of the wait method is to block the current thread, wait for the wake-up of the notify/notifyAll method, or wait for the Automatic wake-up after the timeout.
2, notify / notifyAll methodWith an understanding of the principle of the wait method, the notify method and notifyAll method are easy to understand.
Since the wait method is implemented through the monitor object of the object, as long as the notify/notifyAll method is called on the same object, the waiting thread on the corresponding object monitor can be awakened.
The difference between notify and notifyAll is that the former can only wake up one thread on the monitor without affecting other threads, while notifyAll wakes up all threads
It is easy to understand the difference between the two by looking at the following examples:
package com.paddx.test.concurrent; public class NotifyTest { public synchronized void testWait(){ System.out.println(Thread.currentThread().getName() +" Start-----"); try { wait(0); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() +" End-------"); } public static void main(String[] args) throws InterruptedException { final NotifyTest test = new NotifyTest(); for(int i=0;i<5;i++) { new Thread(new Runnable() { @Override public void run() { test.testWait(); } }).start(); } synchronized (test) { test.notify(); } Thread.sleep(3000); System.out.println("-----------Split line-------------"); synchronized (test) { test.notifyAll(); } } }
The output results are as follows:
Thread-0 Start----- Thread-1 Start----- Thread-2 Start----- Thread-3 Start----- Thread-4 Start----- Thread-0 End------- -----------Split line------------- Thread-4 End------- Thread-3 End------- Thread-2 End------- Thread-1 End-------
It can be seen from the results that only Thread-0 is awakened when notifymethod is called, but all threads are awakened when notifyAll is called.
Finally, there are two points to note:
- After calling the wait method, the thread will release its ownership of the monitor object.
- A thread blocked by the wait method must meet the following two conditions at the same time before it can be truly executed:
- The thread needs to be woken up (wake up timeout or call notify/notifyll).
- After the thread wakes up, it needs to compete for a lock (monitor).
The join method is used to wait for the parent thread to execute after the child thread completes execution. In other words, it is used to merge asynchronous threads into synchronous threads.
The JDK provides three versions of the join method. Its implementation is similar to that of the wait method. The join() method actually executes the join(0), and the implementation of the join(long millis, int nanos) is consistent with that of the wait(long millis, int nanos). The nanosecond support is not complete for the time being.
We can look at the source code of the join method to make it easier to understand:
public final void join() throws InterruptedException { join(0); } public final synchronized void join(long millis) throws InterruptedException { long base = System.currentTimeMillis(); long now = 0; if (millis < 0) { throw new IllegalArgumentException("timeout value is negative"); } if (millis == 0) { while (isAlive()) { wait(0); } } else { while (isAlive()) { long delay = millis - now; if (delay <= 0) { break; } wait(delay); now = System.currentTimeMillis() - base; } } } public final synchronized void join(long millis, int nanos) throws InterruptedException { if (millis < 0) { throw new IllegalArgumentException("timeout value is negative"); } if (nanos < 0 || nanos > 999999) { throw new IllegalArgumentException( "nanosecond timeout value out of range"); } if (nanos >= 500000 || (nanos != 0 && millis == 0)) { millis++; } join(millis); }
Let's focus on the implementation of the join(long millis) method. It can be seen that the join method blocks the thread through the wait method. If the join thread is still executing, block the current thread until the join thread completes execution.
However, it should be noted that the join here only calls the wait method, but there is no corresponding notify method. The reason is that the corresponding processing is done in the start method of Thread. Therefore, when the join Thread completes execution, it will automatically wake up the main Thread to continue execution.
Example: demonstrate the role of the join method
(1) Do not use the join method:
package com.paddx.test.concurrent; public class JoinTest implements Runnable{ @Override public void run() { try { System.out.println(Thread.currentThread().getName() + " start-----"); Thread.sleep(1000); System.out.println(Thread.currentThread().getName() + " end------"); } catch (InterruptedException e) { e.printStackTrace(); } } public static void main(String[] args) { for (int i=0;i<5;i++) { Thread test = new Thread(new JoinTest()); test.start(); } System.out.println("Finished~~~"); } }
The results are as follows:
Thread-0 start----- Thread-1 start----- Thread-2 start----- Thread-3 start----- Finished~~~ Thread-4 start----- Thread-2 end------ Thread-4 end------ Thread-1 end------ Thread-0 end------ Thread-3 end------
(2) Use the join method:
package com.paddx.test.concurrent; public class JoinTest implements Runnable{ @Override public void run() { try { System.out.println(Thread.currentThread().getName() + " start-----"); Thread.sleep(1000); System.out.println(Thread.currentThread().getName() + " end------"); } catch (InterruptedException e) { e.printStackTrace(); } } public static void main(String[] args) { for (int i=0;i<5;i++) { Thread test = new Thread(new JoinTest()); test.start(); try { test.join(); //Call the join method } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("Finished~~~"); } }
The results are as follows:
Thread-0 start----- Thread-0 end------ Thread-1 start----- Thread-1 end------ Thread-2 start----- Thread-2 end------ Thread-3 start----- Thread-3 end------ Thread-4 start----- Thread-4 end------ Finished~~~
Comparing the execution results of the two pieces of code, it is easy to find that threads execute concurrently without using the join method, while all threads execute sequentially after using the join method.
reference resources: https://www.cnblogs.com/paddix/p/5381958.html