preface
The previous several articles in-depth analysis of Thread, synchronized, AQS and other related knowledge, the foundation has been laid, the next to analyze the application of several common methods, principles and easy to confuse. Through this article, you will learn:
1, Thread.sleep application and principle 2, thread. yield application and principle 3, thread. join application and principle 4, Object.wait application and principle 5, Condition. Await application and principle 6, Summary
1. Application and principle of Thread.sleep
Thread.sleep applications
Thread.sleep role:
After thread. sleep is called, the Thread goes to sleep and gives up the CPU, waits for the timeout to run out, and is scheduled to run again by the CPU.
Let’s start with the definition in Thread. Java:
public static native void sleep(long millis) throws InterruptedException;
Copy the code
As you can see, it accepts a timeout in milliseconds and may throw InterruptedException. Of course, you can also pass:
public static void sleep(long millis, int nanos) throws InterruptedException{... }Copy the code
Chase Ghana in seconds. Look at the Demo:
private void testSleep() { try { Thread.sleep(-1); //----->(1) Thread.sleep(0); //------>(2) Thread.sleep(1); //------>(3) } catch (InterruptedException e) { e.printStackTrace(); }}Copy the code
If (1)/(2)/(3) is passed a negative number, a 0 number, and a positive number, what are the results? (1) IllegalArgumentException will be thrown. (2) Normal operation. (3) The thread can continue to execute after 1ms sleep.
Thread.sleep principle
We then look at the different arguments thread.sleep (xx) receives and why it responds to interrupts from a source code perspective. Thread.sleep(XX) is a native method, and its corresponding JNI function mapping is found first:
#Thread.c
static JNINativeMethod methods[] = {
{"start0", "()V", (void *)&JVM_StartThread},
{"stop0", "(" OBJ ")V", (void *)&JVM_StopThread},
{"isAlive", "()Z", (void *)&JVM_IsThreadAlive},
{"suspend0", "()V", (void *)&JVM_SuspendThread},
{"resume0", "()V", (void *)&JVM_ResumeThread},
{"setPriority0", "(I)V", (void *)&JVM_SetThreadPriority},
{"yield", "()V", (void *)&JVM_Yield},
{"sleep", "(J)V", (void *)&JVM_Sleep},
{"currentThread", "()" THD, (void *)&JVM_CurrentThread},
{"countStackFrames", "()I", (void *)&JVM_CountStackFrames},
{"interrupt0", "()V", (void *)&JVM_Interrupt},
{"isInterrupted", "(Z)Z", (void *)&JVM_IsInterrupted},
{"holdsLock", "(" OBJ ")Z", (void *)&JVM_HoldsLock},
{"getThreads", "()[" THD, (void *)&JVM_GetAllThreads},
{"dumpThreads", "([" THD ")[[" STE, (void *)&JVM_DumpThreads},
{"setNativeName", "(" STR ")V", (void *)&JVM_SetNativeThreadName},
};
Copy the code
C++ function implementation:
#jvm.cpp JVM_ENTRY(void, JVM_Sleep(JNIEnv* env, jclass threadClass, jlong millis)) JVMWrapper("JVM_Sleep"); If (millis < 0) {// If the time passed is negative, THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), "timeout value is negative"); If (Thread::is_interrupted (Thread, true) &&! If (Thread::is_interrupted (Thread, true) &&! HAS_PENDING_EXCEPTION) { THROW_MSG(vmSymbols::java_lang_InterruptedException(), "sleep interrupted"); }... If (millis == 0) {// the incoming time is 0 //ConvertSleepToYield specifies whether to ConvertSleepToYield, Under x86 true if (ConvertSleepToYield) {// Call the system yield() function OS ::yield(); } else { ... MinSleepInterval(1ms) OS ::sleep(thread, MinSleepInterval, false); . If (OS ::sleep(thread, millis, true) == OS_INTRPT) { Throws an interrupt exception THROW_MSG(vmSymbols::java_lang_InterruptedException(), "sleep Interrupted "); } } thread->osthread()->set_state(old_state); } if (event.should_commit()) { event.set_time(millis); event.commit(); } #ifndef USDT2 JVM_ENDCopy the code
At this point, the above doubts are basically solved.
Thread.sleep(-1), thread. sleep(-1), thread. sleep(-1), thread. sleep(-1) Thread.sleep(0), thread.yield (); Park (xx)–> OS ::Linux:: safe_cond_timedWait ()–> nptl. pthread_cond_timedWait (xx) suspends the thread for a limited time. 4, Thread.sleep(xx), if an interrupt occurs, throws an interrupt exception and marks the interrupt position as false.
2, Thread.yield application and principle
Thread. The yield
Thread. The yield function:
When thread. yield is called, the Thread yields the CPU, and the CPU schedules a Thread with a higher or equal priority than the current Thread. If not, the thread. yield call returns immediately.
In concurrent scenarios, CPU utilization can be improved.
Let’s start with the definition in Thread. Java:
public static native void yield();
Copy the code
If a node is in sync with a queue, Thread. Yield if a node is in sync with a queue.
#AbstractQueuedSynchronizer.java final boolean transferAfterCancelledWait(Node node) { ... // check if the queue is synchronized while (! IsOnSyncQueue (node)) // Instead of endless rotation, thread.yield (); return false; }Copy the code
For example, Thread A puts A node in A synchronous queue, and Thread B queries it. Since Thread A and Thread B are not strictly synchronous, there is no wait/notification mechanism. Thread B knows that Thread A puts A node quickly, so Thread B simply needs thread.yield () to yield the CPU.
Thread. The principle of yield
#jvm.cpp JVM_ENTRY(void, JVM_Yield(JNIEnv *env, jclass threadClass)) JVMWrapper("JVM_Yield"); If (OS ::dont_yield()) return; if (OS ::dont_yield()) return; //ConvertYieldToSleep default false if (ConvertYieldToSleep) {OS ::sleep(thread, MinSleepInterval, false); } else {yield OS ::yield(); } JVM_END #os_linux.cpp void OS ::yield() {// sched_yield(); }Copy the code
As you can see, Thread.yield does not respond to interrupts.
3, The application and principle of thread. join
Thread. Join the application
Thread. The join function:
After thread. join is called, the caller Thread blocks and waits until the target Thread stops running before returning.
It can be used for simple synchronization between threads, such as thread A (caller thread) waiting for thread B(target thread) to finish executing before doing other actions.
Definitions in Thread. Java:
public final void join() throws InterruptedException(){... }Copy the code
You can also set the timeout period. If the timeout period is exhausted and the target Thread is not finished executing the thread. join, you can return the Thread in advance.
Public final synchronized void join(long millis){... }Copy the code
Look at the Demo:
public class TestThread { public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } system.out. println("t1 is running to the end "); }}); t1.start(); System.out.println(" wait for t1 to complete "); t1.join(); System.out.println("t1 execution finished "); }}Copy the code
The main Thread calls Thread.join and waits for T1 to complete.
Thread. The principle of the join
#Thread. Java public final void join() throws InterruptedException {// Timeout duration 0 join(0); } public final synchronized void join(long millis) throws InterruptedException { long base = System.currentTimeMillis(); long now = 0; If (millis < 0) {// The timeout period must >=0 throw new IllegalArgumentException("timeout value is negative"); } if (millis == 0) {while (isAlive()) {this.wait(0), thread.wait(); } } else { while (isAlive()) { long delay = millis - now; If (delay <= 0) {// Delay <= 0; } wait(delay); now = System.currentTimeMillis() - base; }}}Copy the code
It can be seen that Thread. Join with the aid of the Object. Wait/Object. Notify to achieve synchronization between threads. Thread.join has no explicit interrupt logic, but it does declare that it needs to throw an interrupt exception, which is actually thrown by Object.wait. If you want thread. wait to return, you need to call thread. notify. Can only be called at the end of a thread.
Void JavaThread::exit(bool destroy_vm, ExitType exit_type) {... ensure_join(this); . } static void ensure_join(JavaThread* thread) { ... ObjectLocker lock(threadObj, thread); . // Wake up the threads waiting on the thread object lock.notify_all(thread); . } #ObjectSynchronizer.hpp void notify_all(TRAPS) { ObjectSynchronizer::notifyall(_obj, CHECK); }Copy the code
As you can see, thread. Notify_all is finally called after the thread terminates to wake up all the threads waiting for it to complete. Because Thread.join calls Object.wait, and Object.wait needs to throw an interrupt exception, Thread.join also needs to throw an interrupt exception.
4. Application and principle of Object.wait
The application Object. Wait
Object. Wait function:
After calling Object.wait, the thread blocks and waits for another thread to wake it up.
For synchronization between threads.
Let’s look at the definition in Object.java:
public final void wait() throws InterruptedException
Copy the code
You can see that this method might throw an interrupt exception. Of course, wait(xx) can set timeout.
public final native void wait(long timeout) throws InterruptedException;
Copy the code
The timeout period is expressed in milliseconds (ms).
Look at the Demo:
public class TestThread { public static Object object = new Object(); public static volatile boolean flag = false; public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(new Runnable() { @Override public void run() { synchronized (object) { try { System.out.println("flag is:" + flag + " t1 wait"); while (! flag) object.wait(); System.out.println("flag is:" + flag + " t1 continue"); } catch (InterruptedException e) { e.printStackTrace(); }}}}); t1.start(); Thread.sleep(2000); synchronized (object) { System.out.println("flag is:" + flag + " main change flag"); flag = true; object.notify(); System.out.println("flag is:" + flag + " main notify t1"); }}}Copy the code
T1 finds that flag==false and calls wait for flag to change to true. The main thread changes flag to true and calls notify to wake UP T1. This completes a simple communication between threads (synchronization).
The principle of the Object. Wait
Object. Wait source code in the previous Java Synchronized heavyweight lock principle in-depth analysis (synchronization) has been analyzed, this focus on the analysis of how it is to respond to the interrupt.
CPP void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) {// If an interrupt occurs, If (interruptible && Thread::is_interrupted(Self, true) &&! HAS_PENDING_EXCEPTION) { ... THROW(vmSymbols::java_lang_InterruptedException()); return ; }... // Release lock exit (true, Self); { { if (interruptible && (Thread::is_interrupted(THREAD, False) | | HAS_PENDING_EXCEPTION)) {/ / interrupt} else if (node. _notified = = 0) {/ / hung thread, If (millis <= 0) {Self->_ParkEvent->park (); } else { ret = Self->_ParkEvent->park (millis) ; }}}} // If (v == ObjectWaiter::TS_RUN) {... } else {// ReenterI (Self, &node); node.wait_reenter_end(this); } if (! WasNotified) { if (interruptible && Thread::is_interrupted(Self, true) && ! HAS_PENDING_EXCEPTION) {THROW(vmSymbols::java_lang_InterruptedException()); vmSymbols::java_lang_InterruptedException()); }}}Copy the code
Call object.wait () :
The thread releases the lock. 2, the thread calls parkevent.park (xx)–> OS ::Linux:: safe_cond_timedWait ()–> nptl. pthread_cond_timedWait (xx) to suspend the thread. 3. Threads wake up and continue fighting for locks. 4. If an interrupt occurs in phases 1, 2, and 3, reset the interrupt flag bit and throw an interrupt exception.
5. Condition. Await application and principle
Condition. Await application
Condition. Await: same as object. wait.
Look at the definition in condition.java:
void await() throws InterruptedException;
Copy the code
As you can see, an interrupt exception might be thrown. Of course await(xx) can be set to timeout.
boolean await(long time, TimeUnit unit) throws InterruptedException;
Copy the code
The value can be milliseconds or nanoseconds.
Look at the Demo:
public class TestThread { public static Lock myLock = new ReentrantLock(); public static Condition condition = myLock.newCondition(); public static volatile boolean flag = false; public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(new Runnable() { @Override public void run() { myLock.lock(); try { System.out.println("flag is:" + flag + " t1 await"); while (! flag) condition.await(); System.out.println("flag is:" + flag + " t1 continue"); } catch (InterruptedException e) { } finally { myLock.unlock(); }}}); t1.start(); Thread.sleep(2000); myLock.lock(); try { System.out.println("flag is:" + flag + " main change flag"); flag = true; condition.signal(); System.out.println("flag is:" + flag + " main signal t1"); } catch (Exception e) { } finally { myLock.unlock(); }}}Copy the code
Wait Demo is similar to Object.wait Demo, except object. wait needs to be used with synchronized, while condition. await needs to be used with Lock.
Condition. Await principle
AQS analysis of Java concurrency
6, summary
Thread.sleep/Thread. Join/Thread. Yield and Lock has nothing to do, and the Object. The wait call need to obtain before synchronized Lock, Condition. Await calls need to acquire the Lock Lock before, so they have relations and Lock.
It’s easy to mix them up because, on the surface, calling each of these methods will block the thread. Except for thread.yield, all methods block threads by calling the underlying NPTL corresponding function.
Finally, summarize the analysis with a graph:
What needs to be explained here is:
We pay attention to any relationship is based on the external lock the lock, critical region execution Thread. Sleep/Thread. The yield/Thread. Join/Object. Wait/Condition. The methods of await any association with lock. Synchronized Object. Wait is used internally. Synchronized Object.
The next chapter analyzes the principles and applications of ReentrantLock and ReentrantReadWriteLock. This article is based on JDK1.8.