Conversion of Java Thread State and API

Keywords: Programming Java less snapshot

Conversion of Java Thread State and API

Thread state

The most authoritative information refers to the internal enumeration of java.lang.Thread$State in the official Thread source. The description simply lacks a more detailed description of the state of runnable, which should be split into read and run.

In addition to the official information, the following will also synthesize several blog information maps. The names of some states may be confused, there are many names, and even differences in meaning, which need to be understood by the readers themselves.

Thread state transition

Note: The official runnable status is divided into ready and running. Some materials also call readiness operational, that is to say, runnable is directly considered as ready. The official runnable is actually the collective name of read and running.

  • After the thread is instantiated, it enters new
  • The thread in the new state starts () and enters ready, which is the state selected by the CPU.
  • Threads in read state are selected by CPU to run
  • If running encounters synchronized, enter blocked
  • If wait(), running gives up the lock and enters wait().
  • running, if sleep(timeout), enters timed_waiting (with a timeout waiting state)
  • running lets time slices or time slices run out if yield(), and enters ready (yielding may be selected as soon as it is released).
  • If running is finished, enter terminated
  • blocked threads, after getting the lock, enter ready (not immediately running after getting the lock)
  • A thread that waits and timed_waiting enters ready after notification by other threads (not immediately available after notification)

Additional explanation

The above version is a simple version.

  • yield() yields time slices to other threads with the same priority
  • After sleep(timeout), other threads with arbitrary priority are likely to get opportunities
  • Why wait()/notify()/notifyAll() method is Object rather than thread, because the lock object can be arbitrary Object (including java.lang.Thread object). As long as the thread competes with the same Object, that is, the same lock, the code locked with this lock can ensure that only one locked thread can enter the synchronization block at the same time. In addition, different threads communicate with each other using wait()/notify()/notifyAll() of the same lock object. There is always something in common between threads. This common thing is the lock.

Supplementary Figure 1:

Supplementary Figure 2:

Thread API

Summary

(no)

API

Some are Object's interfaces, some are Thread's, and some are LockSupport's.

1,wait(timeout)/wait()

public final native void wait(long timeout) throws InterruptedException

public final void wait() throws InterruptedException,Transfer with reference Edition

Object method, this object is the so-called lock object, in fact, the real lock object should be Object's monitor, right? So the monitor description here is generally referred to as "lock" or "lock object".

  • Calling wait() is equivalent to wait(0), which is a native method

  • Calling wait(timeout) indicates that the current thread enters and waits, and releases the monitors it holds until one of the following four scenarios occurs:

    • Other threads notify and are randomly selected as the only awakened thread
    • Other threads notify all
    • Other threads interrupt this thread
    • If timeout > 0, the specified time is up; if timeout=0, this rule is invalid

    When the thread is awakened, it competes with other threads equally and indiscriminately for locks until the lock is acquired and then continues to run from the breakpoint.

  • The current thread must have a lock (that is, the monitor for the Object), that is, it must be in a synchronized method or block of code to use the wait method.

  • Be careful to use while for conditional judgment, not if, because threads may interrupt and spurious.

synchronized (obj) {
     while (<condition does not hold>)
         obj.wait(timeout);
     ... // Perform action appropriate to condition
 }
/**
 * Causes the current thread to wait until either another thread invokes the
 * {@link java.lang.Object#notify()} method or the
 * {@link java.lang.Object#notifyAll()} method for this object, or a
 * specified amount of time has elapsed.
 * <p>
 * The current thread must own this object's monitor.
 * <p>
 * This method causes the current thread (call it <var>T</var>) to
 * place itself in the wait set for this object and then to relinquish
 * any and all synchronization claims on this object. Thread <var>T</var>
 * becomes disabled for thread scheduling purposes and lies dormant
 * until one of four things happens:
 * <ul>
 * <li>Some other thread invokes the {@code notify} method for this
 * object and thread <var>T</var> happens to be arbitrarily chosen as
 * the thread to be awakened.
 * <li>Some other thread invokes the {@code notifyAll} method for this
 * object.
 * <li>Some other thread {@linkplain Thread#interrupt() interrupts}
 * thread <var>T</var>.
 * <li>The specified amount of real time has elapsed, more or less.  If
 * {@code timeout} is zero, however, then real time is not taken into
 * consideration and the thread simply waits until notified.
 * </ul>
 * The thread <var>T</var> is then removed from the wait set for this
 * object and re-enabled for thread scheduling. It then competes in the
 * usual manner with other threads for the right to synchronize on the
 * object; once it has gained control of the object, all its
 * synchronization claims on the object are restored to the status quo
 * ante - that is, to the situation as of the time that the {@code wait}
 * method was invoked. Thread <var>T</var> then returns from the
 * invocation of the {@code wait} method. Thus, on return from the
 * {@code wait} method, the synchronization state of the object and of
 * thread {@code T} is exactly as it was when the {@code wait} method
 * was invoked.
 * <p>
 * A thread can also wake up without being notified, interrupted, or
 * timing out, a so-called <i>spurious wakeup</i>.  While this will rarely
 * occur in practice, applications must guard against it by testing for
 * the condition that should have caused the thread to be awakened, and
 * continuing to wait if the condition is not satisfied.  In other words,
 * waits should always occur in loops, like this one:
 * <pre>
 *     synchronized (obj) {
 *         while (&lt;condition does not hold&gt;)
 *             obj.wait(timeout);
 *         ... // Perform action appropriate to condition
 *     }
 * </pre>
 * (For more information on this topic, see Section 3.2.3 in Doug Lea's
 * "Concurrent Programming in Java (Second Edition)" (Addison-Wesley,
 * 2000), or Item 50 in Joshua Bloch's "Effective Java Programming
 * Language Guide" (Addison-Wesley, 2001).
 *
 * <p>If the current thread is {@linkplain java.lang.Thread#interrupt()
 * interrupted} by any thread before or while it is waiting, then an
 * {@code InterruptedException} is thrown.  This exception is not
 * thrown until the lock status of this object has been restored as
 * described above.
 *
 * <p>
 * Note that the {@code wait} method, as it places the current thread
 * into the wait set for this object, unlocks only this object; any
 * other objects on which the current thread may be synchronized remain
 * locked while the thread waits.
 * <p>
 * This method should only be called by a thread that is the owner
 * of this object's monitor. See the {@code notify} method for a
 * description of the ways in which a thread can become the owner of
 * a monitor.
 *
 * @param      timeout   the maximum time to wait in milliseconds.
 * @throws  IllegalArgumentException      if the value of timeout is
 *               negative.
 * @throws  IllegalMonitorStateException  if the current thread is not
 *               the owner of the object's monitor.
 * @throws  InterruptedException if any thread interrupted the
 *             current thread before or while the current thread
 *             was waiting for a notification.  The <i>interrupted
 *             status</i> of the current thread is cleared when
 *             this exception is thrown.
 * @see        java.lang.Object#notify()
 * @see        java.lang.Object#notifyAll()
 */
public final native void wait(long timeout) throws InterruptedException;

2,notify()

public final native void notify()

  • Notify the waiting threads on the Object, and if there are more than one, any one will be notified.
  • The current thread must have the lock of the Object, that is, notify must be written in a synchronized method or block of code
  • When notify is invoked, the current thread does not release the lock immediately until the synchronization method or block of synchronization code is executed.
  • The notified thread must wait for the lock to resume running until it calls wait().
  • Notified threads will compete for locks without advantages or disadvantages. Fair competition
/**
 * Wakes up a single thread that is waiting on this object's
 * monitor. If any threads are waiting on this object, one of them
 * is chosen to be awakened. The choice is arbitrary and occurs at
 * the discretion of the implementation. A thread waits on an object's
 * monitor by calling one of the {@code wait} methods.
 * <p>
 * The awakened thread will not be able to proceed until the current
 * thread relinquishes the lock on this object. The awakened thread will
 * compete in the usual manner with any other threads that might be
 * actively competing to synchronize on this object; for example, the
 * awakened thread enjoys no reliable privilege or disadvantage in being
 * the next thread to lock this object.
 * <p>
 * This method should only be called by a thread that is the owner
 * of this object's monitor. A thread becomes the owner of the
 * object's monitor in one of three ways:
 * <ul>
 * <li>By executing a synchronized instance method of that object.
 * <li>By executing the body of a {@code synchronized} statement
 *     that synchronizes on the object.
 * <li>For objects of type {@code Class,} by executing a
 *     synchronized static method of that class.
 * </ul>
 * <p>
 * Only one thread at a time can own an object's monitor.
 *
 * @throws  IllegalMonitorStateException  if the current thread is not
 *               the owner of this object's monitor.
 * @see        java.lang.Object#notifyAll()
 * @see        java.lang.Object#wait()
 */
public final native void notify();

3,notifyAll()

public final native void notifyAll()

  • Just like notify, the only difference is that it notifies all the threads waiting on Object.

4,join(timeout)/join()

public final synchronized void join(long millis) throws InterruptedException

public final void join() throws InterruptedException,Transfer with reference Edition

  • The current thread waits for the target thread for a certain period of time, or until the end of the target thread run (die) to continue execution, the two conditions are achieved first.

    For example, the code t.join() means the current thread, that is, the thread executing the t.join() code (assuming M) must wait until the t thread has finished executing before the code following t.join() can continue to be executed by M.

    Join means to join t into M. As long as t is not finished, M is stuck in t.join(), which is the meaning of join.

  • The above-mentioned join() is equivalent to join(0).

  • join() is usually used in main threads, where all other threads finish executing and then terminate the main threads

  • Implement: It uses wait internally, non-native method.

    Quite simply, the main thread is M, executing t.join(0), the join method is synchronous, M obtains the lock (t is the lock object), isAlive() judges whether T is still alive, and if it is not finished, executes wait(0), then M releases the lock and enters the wait queue. Execute notifyAll of T when t runs out (in native code, refer to) Here In this way, the execution of the M thread is restored. In this way, M and t are executed before execution.

Reflection:

In fact, join(timeout) is really like sleep(timeout). The difference is that join(timeout) does not have to wait for timeout, but can terminate the wait after the end of the target thread. And join() can achieve infinite and so on, while sleep does not correspond. In addition, they are different in mechanism.

Write examples to help understand join

There are three ways for the main thread to finish executing until all other threads have finished.

  • Using join()
  • Use CountDownLatch
  • Use yield()

Using join()

// Conclusion: The main thread does not wait for the t thread to complete, but prints the result first.
// After opening the code t.join(), the current thread (main thread)
// It waits for the target thread (t, the method of join() to finish executing before continuing.
// The so-called "finished execution" means that the run method of the t thread executes and the thread dies.
public static void main(String[] args) throws Exception {

	Thread t = new Thread(new Runnable() {

		@Override
		public void run() {
			for (int i = 0; i < 5; i++) {
				System.out.println("threadName:" + Thread.currentThread().getName() + ",i= " + i);

				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
	});
	t.start();
    // t.join();
	System.out.println("threadName:" + Thread.currentThread().getName());
}

Use yield()

// You can also replace the above code t.join() with the following.
// There are two active threads in the main thread plus t thread. Active means not dead yet.
// If t runs out and dies, activeCount becomes 1, then it jumps out of the loop.
// Note: When I learned yield() earlier, the API document said yield() is a signal, and the scheduler
// It can be ignored. But that's okay, because it's a while loop, and what's missing is that it's still in the loop.
while(Thread.activeCount() > 1) {
	Thread.yield();
}

Use CountDownLatch

// PS: CountDownLatch allows multithreaded operations
public class JoinTest {
	private static CountDownLatch countDownLatch = new CountDownLatch(1);
	
	public static void main(String[] args) throws Exception {

		Thread t = new Thread(new Runnable() {

			@Override
			public void run() {
				for (int i = 0; i < 5; i++) {
					System.out.println("threadName:" + Thread.currentThread().getName() + ",i= " + i);

					try {
						Thread.sleep(100);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				
				// End of Thread run Method
				countDownLatch.countDown();
			}
		});
		t.start();
		
		countDownLatch.await();
		
		
		System.out.println("threadName:" + Thread.currentThread().getName());
	}
}

Source code for join(timeout)

/**
 * Waits at most {@code millis} milliseconds for this thread to
 * die. A timeout of {@code 0} means to wait forever.
 *
 * <p> This implementation uses a loop of {@code this.wait} calls
 * conditioned on {@code this.isAlive}. As a thread terminates the
 * {@code this.notifyAll} method is invoked. It is recommended that
 * applications not use {@code wait}, {@code notify}, or
 * {@code notifyAll} on {@code Thread} instances.
 *
 * @param  millis
 *         the time to wait in milliseconds
 *
 * @throws  IllegalArgumentException
 *          if the value of {@code millis} is negative
 *
 * @throws  InterruptedException
 *          if any thread has interrupted the current thread. The
 *          <i>interrupted status</i> of the current thread is
 *          cleared when this exception is thrown.
 */
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;
        }
    }
}

5,sleep(timeout)

  • The current thread goes to sleep (pauses execution) and does not give up if it holds a lock
  • sleep() gives low-priority threads the opportunity to execute, and high-priority threads the opportunity to execute.
  • When a thread sleep is interrupt ed, it immediately throws an InterruptedException exception and does not wait until sleep time passes.
# Causes the currently executing thread to sleep (temporarily cease execution) for the specified number of milliseconds,subject to the precision and accuracy of system timers and schedulers: 
* Subject to: Should be "dependent" and "subject to"
* precision and accuracy both mean accuracy, which seems to be biased toward "accuracy"
In general, how long you sleep depends on the accuracy and accuracy of the system's timers and schedulers.

/**
 * Causes the currently executing thread to sleep (temporarily cease
 * execution) for the specified number of milliseconds, subject to
 * the precision and accuracy of system timers and schedulers. The thread
 * does not lose ownership of any monitors.
 *
 * @param  millis
 *         the length of time to sleep in milliseconds
 *
 * @throws  IllegalArgumentException
 *          if the value of {@code millis} is negative
 *
 * @throws  InterruptedException
 *          if any thread has interrupted the current thread. The
 *          <i>interrupted status</i> of the current thread is
 *          cleared when this exception is thrown.
 */
public static native void sleep(long millis) throws InterruptedException;

6,yield()

  • Approaches that might be more useful for debug
  • Abandoning the current CPU slice will not release the lock
  • First check whether the current thread with the same priority is in a runnable state. If so, give the CPU ownership to the thread. Otherwise, continue to run the original thread. So yield() method is called "yielding". It only gives the running opportunity to other threads of the same level.
# It is seldom appropriate to use this method: It should mean that there is a lot of use of this method (scenario), that is, "this method is seldom used."

# The scheduler is free to ignore this hint: 
The scheduler can ignore the prompt, be free to, do it freely, you can do it or not.

# Yield is a heuristic attempt to improve relative progression
between threads that would otherwise over-utilise a CPU
 * heuristic attempt: heuristic attempt, because the previous sentence "the scheduler may ignore yield", so this is a heuristic, not mandatory.
* relative progression: Relative development, that is to say, don't execute a lot of threads A and B. Threads B are very few. Everyone develops on average.

yield is a reminder attempt to improve relative development between processes (even point development), otherwise these processes will overuse the CPU.


 
 
/**
 * A hint to the scheduler that the current thread is willing to yield
 * its current use of a processor. The scheduler is free to ignore this
 * hint.
 *
 * <p> Yield is a heuristic attempt to improve relative progression
 * between threads that would otherwise over-utilise a CPU. Its use
 * should be combined with detailed profiling and benchmarking to
 * ensure that it actually has the desired effect.
 *
 * <p> It is rarely appropriate to use this method. It may be useful
 * for debugging or testing purposes, where it may help to reproduce
 * bugs due to race conditions. It may also be useful when designing
 * concurrency control constructs such as the ones in the
 * {@link java.util.concurrent.locks} package.
 */
public static native void yield();

7,interrupt()

public void interrupt()

(briefly, to be added)

/**
 * Interrupts this thread.
 *
 * <p> Unless the current thread is interrupting itself, which is
 * always permitted, the {@link #checkAccess() checkAccess} method
 * of this thread is invoked, which may cause a {@link
 * SecurityException} to be thrown.
 *
 * <p> If this thread is blocked in an invocation of the {@link
 * Object#wait() wait()}, {@link Object#wait(long) wait(long)}, or {@link
 * Object#wait(long, int) wait(long, int)} methods of the {@link Object}
 * class, or of the {@link #join()}, {@link #join(long)}, {@link
 * #join(long, int)}, {@link #sleep(long)}, or {@link #sleep(long, int)},
 * methods of this class, then its interrupt status will be cleared and it
 * will receive an {@link InterruptedException}.
 *
 * <p> If this thread is blocked in an I/O operation upon an {@link
 * java.nio.channels.InterruptibleChannel InterruptibleChannel}
 * then the channel will be closed, the thread's interrupt
 * status will be set, and the thread will receive a {@link
 * java.nio.channels.ClosedByInterruptException}.
 *
 * <p> If this thread is blocked in a {@link java.nio.channels.Selector}
 * then the thread's interrupt status will be set and it will return
 * immediately from the selection operation, possibly with a non-zero
 * value, just as if the selector's {@link
 * java.nio.channels.Selector#wakeup wakeup} method were invoked.
 *
 * <p> If none of the previous conditions hold then this thread's interrupt
 * status will be set. </p>
 *
 * <p> Interrupting a thread that is not alive need not have any effect.
 *
 * @throws  SecurityException
 *          if the current thread cannot modify this thread
 *
 * @revised 6.0
 * @spec JSR-51
 */
public void interrupt() {

8,interrupted() / isInterrupted()

public static boolean interrupted()

public boolean isInterrupted()

  • isInterrupted(): Member method that checks whether the target thread has been interrupted (e.g., t.isInterrupted checks for t) and does not clear the interrupt flag
  • interrupted(), a static method, checks whether the current thread has been interrupted and clears the interrupt flag
  • The effect of cleaning up interrupt tags is that interrupt tags indicate whether they have been interrupted and become uninterrupted after cleaning up. That is to say, the second call to the interface after cleaning up the tags is uninterrupted. Without labeling, the interface is idempotent, and the same result can be obtained by repeated calls.
/**
 * Tests if some Thread has been interrupted.  The interrupted state
 * is reset or not based on the value of ClearInterrupted that is
 * passed.
 */
private native boolean isInterrupted(boolean ClearInterrupted);

9,isAlive

public final native boolean isAlive()

  • To determine whether the process is alive or not, the thread is alive before it dies, and of course it must be started.
/**
 * Tests if this thread is alive. A thread is alive if it has
 * been started and has not yet died.
 *
 * @return  <code>true</code> if this thread is alive;
 *          <code>false</code> otherwise.
 */
public final native boolean isAlive();

10,activeCount()

public static int activeCount()

  • Gets the number of active processes in the current thread group and its subgroups (direct subgroup, indirect subgroup, recursive accumulation)
  • This method is an estimate because threads are dynamically changing (this is certainly impossible to stop all threads for snapshot calculations)
  • This method is mainly used for debug and monitoring.
/**
 * Returns an estimate of the number of active threads in the current
 * thread's {@linkplain java.lang.ThreadGroup thread group} and its
 * subgroups. Recursively iterates over all subgroups in the current
 * thread's thread group.
 *
 * <p> The value returned is only an estimate because the number of
 * threads may change dynamically while this method traverses internal
 * data structures, and might be affected by the presence of certain
 * system threads. This method is intended primarily for debugging
 * and monitoring purposes.
 *
 * @return  an estimate of the number of active threads in the current
 *          thread's thread group and in any other thread group that
 *          has the current thread's thread group as an ancestor
 */
public static int activeCount() {
    return currentThread().getThreadGroup().activeCount();
}

11,LockSupport.park()

A series of methods

  • public static void park()
  • public static void park(Object blocker)
  • Public static void Park nanos (long nanos), 1 second = 11,000 milliseconds = 11,000,000 microseconds = 11,000,000,000 nanoseconds
  • public static void parkUntil(long deadline), deadline is similar to System. current Time Millis (), in milliseconds, unlike parkNanos.
  • public static void parkNanos(Object blocker, long nanos)
  • public static void parkUntil(Object blocker, long deadline)

The difference is: 1. Whether to take time (designated time or designated time point), 2. blocker

  • If there is no permission, the method will block (the thread goes to sleep), otherwise the method will return immediately.
  • Time-bound versions such as parkNanos, parkUtil, etc. are returned immediately if they are licensed, and when they are not, they are returned after the timeout.
  • The threads that enter dormancy exit when one of the three occurs
    • Other threads call unpark, and the parameters are current threads, such as unpark (current threads)
    • Other threads interrupt the current thread
    • False Awakening
supplement
  • The difference with sleep is

    • sleep must set the time. park is optional.
    • sleep can be awakened by timeouts or interruptions, and park can be awakened by timeouts or unpark s from other threads
  • The difference from wait() is

    • Wait will give up the lock and enter the wait. It must be written in the synchronized method / block, and park does not need it.
    • wait wants other threads to notify, but unpark can be unpark first, and then in its own park, of course, if you first enter the park, you must have other threads to unpark.
  • park and unpark

    • unpark once and park again, then Park won't block
    • You can also unpark many times, in park, the park will not block.
    • unpark once, park again will not block, park again will block, because the license has been consumed (which is why the API document uses the word consumed, will be consumed)
    • Unpark twice, and then park will not be blocked, and then park is blocked, indicating how many times the consumer money unpark is issued a license
    • Unpark once, park again will not be blocked, unpark again, park again will not be blocked. Explain that the license can be issued again after it is consumed once
public static void main(String[] args) {
	// Print out a, b
	LockSupport.unpark(Thread.currentThread());
	LockSupport.park();
	System.out.println("a");
	
	LockSupport.unpark(Thread.currentThread());
	LockSupport.park();
	System.out.println("b");
}
  • Threads in park s can be interrupt ed, but no exceptions are thrown
// Printing
// t1,park
// t1,park end
public static void main(String[] args) {
	Thread t1 = new Thread(new Runnable() {

		@Override
		public void run() {
			System.out.println("t1,park");
			try {
				LockSupport.park();
			} catch (Exception e) {
				System.out.println("test park Be interrupted");
			}
			System.out.println("t1,park end");

		}
	});
	t1.start();

	try {
		Thread.sleep(2000);
	} catch (InterruptedException e) {
		e.printStackTrace();
	}
	
	t1.interrupt();
}
Disables the current thread for thread scheduling purposes unless the permit is available
 Disable the current thread for thread scheduling purposes unless authorized. For... purposes, for... purposes (phrases, purposes are plurals)

If the permit is available then it is consumed and the call returns immediately
 This means that if there is permission, the method will not block, but return immediately. Selectively ignore some statements, such as `then it is consumed', which means more detailed, `If there is a license, then the license will be used and the method will be returned immediately when it is called'. Here consumed should mean using the license.


/**
 * Disables the current thread for thread scheduling purposes unless the
 * permit is available.
 *
 * <p>If the permit is available then it is consumed and the call
 * returns immediately; otherwise the current thread becomes disabled
 * for thread scheduling purposes and lies dormant until one of three
 * things happens:
 *
 * <ul>
 *
 * <li>Some other thread invokes {@link #unpark unpark} with the
 * current thread as the target; or
 *
 * <li>Some other thread {@linkplain Thread#interrupt interrupts}
 * the current thread; or
 *
 * <li>The call spuriously (that is, for no reason) returns.
 * </ul>
 *
 * <p>This method does <em>not</em> report which of these caused the
 * method to return. Callers should re-check the conditions which caused
 * the thread to park in the first place. Callers may also determine,
 * for example, the interrupt status of the thread upon return.
 */
public static void park() {
    UNSAFE.park(false, 0L);
}

12,LockSupport.unpark(thread)

public static void unpark(Thread thread)

  • If a thread is not licensed, give it permission (that is, if it has, it won't, so the method can be called repeatedly)
  • If the thread is blocked by park, it will not be blocked after calling unpark
  • If the thread has not been blocked by the park, it will be allowed to call the park, so it will not be blocked at that time.
  • Calling unpark(thread) does not have any effect if the thread has not yet start ed
Makes available the permit for the given thread, if it was not already available.
The subject-predicate-object fixed complement grammar of this sentence is difficult to understand.

/**
 * Makes available the permit for the given thread, if it
 * was not already available.  If the thread was blocked on
 * {@code park} then it will unblock.  Otherwise, its next call
 * to {@code park} is guaranteed not to block. This operation
 * is not guaranteed to have any effect at all if the given
 * thread has not been started.
 *
 * @param thread the thread to unpark, or {@code null}, in which case
 *        this operation has no effect
 */
public static void unpark(Thread thread) {
    if (thread != null)
        UNSAFE.unpark(thread);
}

Posted by dombrorj on Sat, 27 Apr 2019 01:48:37 -0700