Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”.

Details the six states of Java threads and transitions between them.

This article is based on JDK1.8.

1 Thread state (lifecycle)

1.1 Status in source code

There are five, six or even seven versions of the Java thread state. This article uses the official Java thread state classification.

In fact, there are officially six thread states, which we can see from the source code:

    public class Thread implements Runnable {
        Thread states are defined as enumerations inside the Thread class
        /**
         * A thread state.  A thread can be in one of the following states:
         * <ul>
         * <li>{@link #NEW}<br>
         *     A thread that has not yet started is in this state.
         *     </li>
         * <li>{@link #RUNNABLE}<br>
         *     A thread executing in the Java virtual machine is in this state.
         *     </li>
         * <li>{@link #BLOCKED}<br>
         *     A thread that is blocked waiting for a monitor lock
         *     is in this state.
         *     </li>
         * <li>{@link #WAITING}<br>
         *     A thread that is waiting indefinitely for another thread to
         *     perform a particular action is in this state.
         *     </li>
         * <li>{@link #TIMED_WAITING}<br>
         *     A thread that is waiting for another thread to perform an action
         *     for up to a specified waiting time is in this state.
         *     </li>
         * <li>{@link #TERMINATED}<br>
         *     A thread that has exited is in this state.
         *     </li>
         * </ul>
         *
         * <p>
         * A thread can be in only one state at a given point in time.
         * These states are virtual machine states which do not reflect
         * any operating system thread states.
         *
         * @since   1.5
         * @see #getState
         */
        public enum State {
            /** * Thread state for a thread which has not yet started. */
            NEW,

            /** * Thread state for a runnable thread. A thread in the runnable * state is executing in the Java virtual machine but it may * be waiting for other resources from the operating system * such as processor. */
            RUNNABLE,

            /**
             * Thread state for a thread blocked waiting for a monitor lock.
             * A thread in the blocked state is waiting for a monitor lock
             * to enter a synchronized block/method or
             * reenter a synchronized block/method after calling
             * {@link Object#wait() Object.wait}.
             */
            BLOCKED,

            /**
             * Thread state for a waiting thread.
             * A thread is in the waiting state due to calling one of the
             * following methods:
             * <ul>
             *   <li>{@link Object#wait() Object.wait} with no timeout</li>
             *   <li>{@link #join() Thread.join} with no timeout</li>
             *   <li>{@link LockSupport#park() LockSupport.park}</li>
             * </ul>
             *
             * <p>A thread in the waiting state is waiting for another thread to
             * perform a particular action.
             * <p>
             * For example, a thread that has called <tt>Object.wait()</tt>
             * on an object is waiting for another thread to call
             * <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
             * that object. A thread that has called <tt>Thread.join()</tt>
             * is waiting for a specified thread to terminate.
             */
            WAITING,

            /**
             * Thread state for a waiting thread with a specified waiting time.
             * A thread is in the timed waiting state due to calling one of
             * the following methods with a specified positive waiting time:
             * <ul>
             *   <li>{@link #sleep Thread.sleep}</li>
             *   <li>{@link Object#wait(long) Object.wait} with timeout</li>
             *   <li>{@link #join(long) Thread.join} with timeout</li>
             *   <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
             *   <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
             * </ul>
             */
            TIMED_WAITING,

            /** * Thread state for a terminated thread. * The thread has completed execution. */TERMINATED; }}Copy the code

As you can see from the source code, the Java language defines six Thread states that are stored as internal enumerations in the Thread class. Here I suggest you use these six states, or you can use your own interpretation of them. Each of these states is explained below.

1.2 Status Description

At any point in time, a thread can have only one of the six states, as follows:

  1. NEW: A thread that has not been started since it was created is in this state.
  2. Run (RUNNABLE) : Calling the start() method,RUNNABLE includes Running and Ready in the operating system thread state, where the thread may be executing or waiting for the CPU to allocate execution time (the thread has acquired resources other than CPU resources). It is not actually running until CPU resources are acquired).

Why don’t the authorities separate these two states? One possibility is that the time between Running and Ready is too short. Modern cpus use polling timeslices, and most threads are Running and Ready for very short periods of time, so consider merging these two states into RUNNABLE states.

  1. WAITING indefinitely: Threads in this state are not allocated CPU time; they are WAITING to be explicitly woken up by another thread. The following methods cause the thread to be stuck in an indefinite wait state:
  1. Object.wait () method with no Timeout parameter.
  2. Thread.join () method with no Timeout parameter.
  3. Locksupport.park () method.
  1. TIMED_WAITING: Threads in this state are also not allocated CPU execution time, but instead of waiting to be explicitly woken up by other threads, they are automatically woken up by the system after a certain time. The following methods cause the thread to enter the finite wait state:
  1. Thread.sleep(long millis) method.
  2. The object.wait () method with the Timeout parameter set.
  3. Thread.join () method with Timeout parameter set.
  4. Locksupport.parknanos () method.
  5. Locksupport.parkuntil () method.
  1. BLOCKED: A thread is BLOCKED. The difference between a BLOCKED state and a waiting state is that a BLOCKED state is waiting to acquire an exclusive lock, an event that can occur when another thread obtains the lock, such as synchronized; The “waiting state” is to release the lock after acquiring it and wait for a period of time, or wait for the awakening action to occur.

  2. TERMINATED: The thread state of a TERMINATED thread. TERMINATED execution is TERMINATED.

Addendum: Java combines the running and ready states of the operating system as the running state.

The blocking state is the state in which a thread blocks when it enters a method or code block modified by the synchronized keyword (obtaining the Lock), whereas the thread state in the java.concurrent package where the Lock interface is blocked is the wait state. Because the Lock interface in the Java.Concurrent package uses the related methods in the LockSupport class for blocking implementations.

2 Thread state conversion

The above six states will be converted to each other when a specific event occurs, and their transformation relationship is shown as follows:

The transitions and methods for the above states are already clear. Here are some of the transitions and the related methods.

2.1 Waiting or Timeout Waiting

2.1.1 Entering the waiting state

  1. LockSupport. Park ().
  2. I/O blocking occurs. Procedure
  3. Suspend (), the method is obsolete.
  4. Public final void wait() Releases the lock
  5. Public final void join() Releases the lock

2.1.1.1 Introduction to Wait methods

The wait() method, which belongs to the Object class, suspends execution of the current thread and releases the lock, allowing other threads to enter a synchronized block and put the current thread into an object wait queue. The Wait() method must be included in the corresponding synchronized statement, and both Wait() and notify() require a monitor of the target object.

When notify() is called, an arbitrary thread is removed from the wait queue of the object and placed in the lock flag wait pool. Only threads in the lock flag wait pool can obtain the lock flag. Notify () does not work if there are no threads in the wait queue. NotifyAll () removes all threads waiting on that object from the object wait pool and places them in the lock flag wait pool.

The awakened thread does not execute immediately but attempts to acquire the lock, and the awakened thread does not release the lock immediately.

2.1.1.2 Introduction to join method

The join method uses wait() internally and wait() underneath to release the lock. The synchronuzed keyword is added to the join method, so there is no problem using wait.

The main function of join method is synchronization. It can change the parallel execution between threads into serial execution, which is similar to synchronization. If thread A calls thread B’s join(), thread A can continue executing only when thread B finishes executing. If there are multiple threads, threads other than THREAD A normally compete for cpus and locks.

If thread A calls thread B’s join(10), thread A waits for thread B to execute for 10 milliseconds. After 10 milliseconds, thread A and thread B execute in parallel. Note that the JDK states that join(0) does not mean that thread A waits for thread B for 0 seconds, but rather that thread A waits for thread B for an infinite amount of time until thread B completes execution, i.e. join(0) is equivalent to join(). Join () calls join(0). When join is called on the main thread, the main thread waits, and the other threads do not need to wait on each other.

Join () differs from synchronized in that join calls wait() internally, while synchronized uses the “object monitor” principle as synchronization.

    /** * join method *@throws InterruptedException
     */
    public final void join(a) throws InterruptedException {
        // Call the join(long millis) method internally with an argument of 0, indicating an infinite wait
        join(0);
    }

    /** * wait for millis milliseconds *@param millis
     * @throws InterruptedException
     */
    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");
        }
        // Millis equals 0 to wait indefinitely
        if (millis == 0) {
            while (isAlive()) {
                // Call the wait method
                wait(0); }}// Otherwise, wait for a limited time
        else {
            while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) {
                    break;
                }
                // Call the wait methodwait(delay); now = System.currentTimeMillis() - base; }}}Copy the code

2.1.2 Entering timeout Wait

  1. Public final void Join (long millis) — Timeout waiting to release the lock
  2. LockSupport.parkNanos
  3. LockSupport.parkUntil
  4. wait(long timeout); — Timeout waiting for lock release
  5. public static void sleep(long millis); — The lock is not released after timeout

2.1.2.1 Introduction to sleep methods

The sleep method pauses the current thread (that is, the thread calling the method) for a period of time to give other threads a chance to continue executing, but it does not release the object lock. That is, if synchronized is fast, other threads still cannot access the shared data. Note that this method catches exceptions. Sleep gives all other threads equal CPU power!

If sleep is interrupted, InterruptedException is thrown at sleep().

If thread.sleep (Long millis) is passed a negative value for the millis argument, an IllegalArgumentException is thrown.

2.1.2.2 Introduction to LockSupport

LockSupport is a thread blocking utility class, all methods are static methods, can let the thread block at any position, of course, after blocking must have a wake up method.

Common methods:

Method names describe
void park() Blocks the current Thread until it returns from park() if the unpark(Thread) method is called or interrupted.
void parkNanos(long nanos) The current thread is blocked and a timeout is returned for a maximum of nanos nanoseconds.
void parkUntil(long deadline) Blocks the current thread until the deadline point
void unpark(Thread) Wake up a blocked thread

Park does not need to acquire a lock on an object. Because Park does not throw InterruptedException on interrupts, park needs to determine the interruption status after the interruption and do additional processing.

2.1.2.3 Outdated suspend and resume methods

The suspend method is not recommended. Suspend () is not recommended because suspend() does not release any lock resources while suspending the thread. No other thread can access the lock it occupies. The suspended thread cannot resume until the corresponding thread executes resume(), and other threads blocked on the lock can resume execution. If the resume() operation occurs before suspend(), the thread is always suspended and holds the lock, which is prone to deadlocks.

Moreover, the thread state of the suspended thread is still RUNNABLE

        // Create a child thread and call suspend inside it to make it "block"
        Thread thread = new Thread(() -> Thread.currentThread().suspend());
        thread.start();
        // The main thread sleeps for three seconds to allow the child threads to run fully
        Thread.currentThread().sleep(3000);
        // The state of the child thread is RUNNABLE
        Thread.State state = thread.getState();
        System.out.println(state);
Copy the code

2.4 Entering the RUNNABLE state

  1. TIMED_WAITING ended. Procedure
  2. WAITING state was awakened. Procedure
  3. BLOCKED acquires the lock
  4. public static void yield();
  5. The thread returns to the ready state after its CPU time slice has been used, but the lock will not be released.

2.4.1 Introduction to yield methods

The yield method, also known as “thread surrender,” causes the calling thread to release CPU execution, allowing it to recontest CPU execution with multiple threads.

Instead of leaving the thread blocked, it is possible that the current compromised thread goes back to the RUNNABLE state and continues running.

Yield tries to give more contention to the same and higher rank threads, while sleep gives equal contention to all threads, but they are not absolute. After all, Java threads are ultimately generated from resources that call the operating system and are fraught with uncertainty.

The yield method does not release the lock that has been held, but only releases the CPU’s execution authority.

If you don’t understand or need to communicate, you can leave a message. In addition, I hope to collect, pay attention to, I will continue to update Java tutorials!