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.

If you like, please like, pay attention to your encouragement is my motivation to move forward

Continue to update, with me step by step system, in-depth study of Java/Android