Java thread lifecycle and state switch

premise

Recently a little lazy, nothing compares depth output. Just want to re-read at the source JUC thread pool to achieve, before the first in-depth look at Java's thread implementation, including the life cycle of the thread, and the thread context switching state switch and so on. Time of this writing, JDK version is 11.

Implementation of the Java threads

In after JDK1.2 , the Java threading model has been identified to achieve the operating system native threading model. Therefore, the current or future JDK version, operating system support how kind of threading model, to a large extent determines how the Java virtual machine thread maps, this is no way to reach an agreement, the virtual machine specification on different platforms nor qualified Java thread needs which threading model to use to achieve. Threading model affects only the size and operating costs of concurrent threads for Java programs, these differences are transparent.

Correspondence Oracle Sun JDKor Oracle Sun JVMin terms of its Windows version and Linux versions are using one thread model implementation (shown below).

That is a Java thread maps to a single LWP ( Light Weight Process ), whereas a lightweight thread in turn mapped to a kernel thread ( Kernel-Level the Thread ). We usually say the thread, often refers to the lightweight processes (or that we usually new java.lang.Threadis the lightweight process instance). Front projection this thread mapping relationship may know, we have created or operated in an application java.lang.Threadexample will eventually be mapped to kernel threads in the system, if we maliciously or experimental unlimited creation java.lang.Threadinstance, will ultimately affect the normal operation of the system and even cause system crashes (you can do experiments in the Windows development environment, be sure to use an infinite loop to create and run memory sufficiently java.lang.Threadinstance).

Thread scheduling mode includes two, cooperative thread scheduling and preemptive thread scheduling.

Thread scheduling description Disadvantaged Advantage
Collaborative thread scheduling Thread execution time is controlled by the thread itself, actively notify the OS switching to another thread after the implementation of If a thread to keep the CPU execution time may cause the entire system to crash Simple, no thread synchronization problems
Preemptive thread scheduling Each thread is allocated by the operating system execution time, thread switching thread itself decide not help To achieve a relatively complex operating system needs to control thread switching and synchronization It will not be a thread blocks cause a system crash

Java threads will eventually be mapped to the system kernel native threads, so Java thread scheduling system ultimately depends on the operating system, and the current mainstream operating system kernel thread scheduling are basically using preemptive thread scheduling. That is what can rote: the Java thread is the use of preemptive thread scheduling mode for thread scheduling .

Many operating systems provide a thread priority concept, but due to problems characteristic of the platform, the Java thread priorities and different platform system thread priorities do not match, so Java thread priorities can only be understood as " recommended priority " , it is popular java.lang.Thread#setPriority(int newPriority)does not necessarily take effect, it is possible Java thread's priority will be to change the system itself .

Java threads state switch

The state of Java threads from java.lang.Threadinside enumeration class java.lang.Thread$Statethat:

public enum State {
      
    NEW,

    RUNNABLE,

    BLOCKED,

    WAITING,

    TIMED_WAITING,

    TERMINATED;
}
复制代码

These states described in FIG summarized as follows:

Relationship between the state of the thread switching is as follows:

By following a few simple API notes and code examples to analyze the meaning and status of the state of Java threads to switch.

NEW state

API Notes :

/**
 * Thread state for a thread which has not yet started.
 *
 */
NEW,
复制代码

Thread instance has not been started when the thread state.

A newly created but not yet started (not yet calling Thread#start()method) is an example of Java threads out of NEWstate.

public class ThreadState {

    public static void main(String[] args) throws Exception {
        Thread thread = new Thread();
        System.out.println(thread.getState());
    }
}

// 输出结果
NEW
复制代码

RUNNABLE state

API Notes :

/**
 * 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,
复制代码

State of the thread under the thread can be running. Runnable threads in the implementation of the Java virtual machine, but it may perform other resources to wait for the operating system, such as the processor.

When Java thread instance calls Thread#start()later, it will enter the RUNNABLEstate. RUNNABLEThe state may be considered include two sub-states: READYand RUNNING.

  • READY: The thread scheduling status may be changed to make it more thread scheduler RUNNINGstate.
  • RUNNING: This state indicates that the thread is running, thread object run()instruction code method is being performed corresponding CPU.

When Java thread instance Thread#yield()method is called thread scheduler or because of scheduling, the state is likely a thread instance RUNNINGinto READY, but the thread state Thread#getState()acquired status remains RUNNABLE. E.g:

public class ThreadState1 {

    public static void main(String[] args) throws Exception {
        Thread thread = new Thread(()-> {
            while (true){
                Thread.yield();
            }
        });
        thread.start();
        Thread.sleep(2000);
        System.out.println(thread.getState());
    }
}
// 输出结果
RUNNABLE
复制代码

WAITING state

API Notes :

   /**
    * 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.
    *
    * For example, a thread that has called {@code Object.wait()}
    * on an object is waiting for another thread to call
    * {@code Object.notify()} or {@code Object.notifyAll()} on
    * that object. A thread that has called {@code Thread.join()}
    * is waiting for a specified thread to terminate.
    */
    WAITING,
复制代码

Wait state thread. A thread into a wait state is due to call one of the following methods:

Without timeout Object # wait ()

Without timeout Thread # join ()

LockSupport.park()

A thread in a wait state is always some special treatment in wait for another thread.

For example: a thread calls the Object # wait (), then it is waiting for Object # notify on another thread calls the object () or Object # notifyAll (); a thread calls Thread # join (), then it is waiting for another a thread end.

WAITINGIs indefinite wait state , the thread in this state will not be assigned CPU execution time. When a thread executes certain methods will enter a waiting state indefinitely until explicitly awakened, after being awakened by a thread state WAITINGmore turns RUNNABLEand then continue.

RUNNABLEConvert WAITINGprocess (wait indefinitely) WAITINGConverted RUNNABLEmethod (wake-up)
Object#wait() Object#notify() | Object#notifyAll()
Thread#join() -
LockSupport.part() LockSupport.unpart(thread)

Wherein the Thread#join()method is relatively specific, it will block a thread until the thread Examples Example finished, its source can be observed as follows:

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;
        }
    }
}
复制代码

Visible Thread#join()is always called when the thread instance survival Object#wait()method, which is to be completed in the thread execution isAlive()for the time false (meaning the thread life cycle has ended) is only unblocked.

Based on WAITINGthe state of an example:

public class ThreadState3 {

    public static void main(String[] args) throws Exception {
        Thread thread = new Thread(()-> {
            LockSupport.park();
            while (true){
                Thread.yield();
            }
        });
        thread.start();
        Thread.sleep(50);
        System.out.println(thread.getState());
        LockSupport.unpark(thread);
        Thread.sleep(50);
        System.out.println(thread.getState());
    }
}
// 输出结果
WAITING
RUNNABLE
复制代码

TIMED WAITING state

API Notes :

/**
* 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,
复制代码

It defines the state of the specific waiting time waiting in the thread. A thread enters this state is due to a specific time-out period specified call one of the following methods:

Thread.sleep()

With a timeout Object # wait ()

With a timeout Thread # join ()

LockSupport.parkNanos()

LockSupport.parkUntil ()

TIMED WAITINGIs there a deadline for wait states , and it WAITINGis somewhat similar thread in this state will not be assigned CPU execution time, but the thread in this state does not need to be explicitly awakened, just waiting for the timeout period will be awakened to reach VM , somewhat similar to alarm a fact of life.

RUNNABLEConverted TIMED WAITINGmethod (there are waiting period) TIMED WAITINGConvert RUNNABLEprocess (timeout wait release)
Object#wait(timeout) -
Thread#sleep(timeout) -
Thread#join(timeout) -
LockSupport.parkNanos(timeout) -
LockSupport.parkUntil(timeout) -

for example:

public class ThreadState4 {

    public static void main(String[] args) throws Exception {
        Thread thread = new Thread(()-> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                //ignore
            }
        });
        thread.start();
        Thread.sleep(50);
        System.out.println(thread.getState());
        Thread.sleep(1000);
        System.out.println(thread.getState());
    }
}
// 输出结果
TIMED_WAITING
TERMINATED
复制代码

BLOCKED state

API Notes :

/**
* 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,
复制代码

This status indicates that a thread is blocked waiting to acquire a monitor lock. The method of synchronization blocks or synchronization code is then re-entry if the thread is blocked, the thread waiting to enter the monitor described lock synchronization or synchronization code block or method call Object # wait ().

BLOCKEDThe state is blocked, the thread in this state will not be assigned CPU execution time. Thread state BLOCKEDwhen there are two possible scenarios:

A thread in the blocked state is waiting for a monitor lock to enter a synchronized block/method

  1. The thread is waiting for a monitor lock, only after obtaining the monitor lock to enter synchronizeda code block or synchronizedmethod, in this process of waiting to acquire the lock thread is blocked.

reenter a synchronized block/method after calling Object#wait()

  1. X thread into synchronizedblocks of code, or synchronizedafter the method call (in this case the monitor lock has been released) Object#wait()by blocking After the method, when receiving a call the other thread lock object T Object#notify()/notifyAll(), but the thread T has not exit it in the synchronizedblock or synchronizedmethod, the thread X still in the blocked state (API note in comments the Reenter , understand it will suddenly see the light scene 2).

A more detailed description can refer to the author previously wrote an article: in-depth understanding of Object provided and wake-up blocking API .

For the above scenario for a simple example:

public class ThreadState6 {

    private static final Object MONITOR = new Object();

    public static void main(String[] args) throws Exception {
        Thread thread1 = new Thread(()-> {
            synchronized (MONITOR){
                try {
                    Thread.sleep(Integer.MAX_VALUE);
                } catch (InterruptedException e) {
                    //ignore
                }
            }
        });
        Thread thread2 = new Thread(()-> {
            synchronized (MONITOR){
                System.out.println("thread2 got monitor lock...");
            }
        });
        thread1.start();
        Thread.sleep(50);
        thread2.start();
        Thread.sleep(50);
        System.out.println(thread2.getState());
    }
}
// 输出结果
BLOCKED
复制代码

For scenario 2 above for a simple example:

public class ThreadState7 {

    private static final Object MONITOR = new Object();
    private static final DateTimeFormatter F = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    public static void main(String[] args) throws Exception {
        System.out.println(String.format("[%s]-begin...", F.format(LocalDateTime.now())));
        Thread thread1 = new Thread(() -> {
            synchronized (MONITOR) {
                System.out.println(String.format("[%s]-thread1 got monitor lock...", F.format(LocalDateTime.now())));
                try {
                    Thread.sleep(1000);
                    MONITOR.wait();
                } catch (InterruptedException e) {
                    //ignore
                }
                System.out.println(String.format("[%s]-thread1 exit waiting...", F.format(LocalDateTime.now())));
            }
        });
        Thread thread2 = new Thread(() -> {
            synchronized (MONITOR) {
                System.out.println(String.format("[%s]-thread2 got monitor lock...", F.format(LocalDateTime.now())));
                try {
                    MONITOR.notify();
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    //ignore
                }
                System.out.println(String.format("[%s]-thread2 releases monitor lock...", F.format(LocalDateTime.now())));
            }
        });
        thread1.start();
        thread2.start();
        // 这里故意让主线程sleep 1500毫秒从而让thread2调用了Object#notify()并且尚未退出同步代码块,确保thread1调用了Object#wait()
        Thread.sleep(1500);  
        System.out.println(thread1.getState());
        System.out.println(String.format("[%s]-end...", F.format(LocalDateTime.now())));
    }
}
// 某个时刻的输出如下:
[2019-06-20 00:30:22]-begin...
[2019-06-20 00:30:22]-thread1 got monitor lock...
[2019-06-20 00:30:23]-thread2 got monitor lock...
BLOCKED
[2019-06-20 00:30:23]-end...
[2019-06-20 00:30:25]-thread2 releases monitor lock...
[2019-06-20 00:30:25]-thread1 exit waiting...
复制代码

Scene 2:

  • Thread 2 calls Object#notify()after sleep 2000 milliseconds, and then exit the synchronized block to release the monitor lock.
  • Thread a sleep call 1,000 milliseconds Object#wait(), this time it has released the lock monitor, the thread 2 sync blocks successfully entered, the thread 1 is described in the API annotation reenter a synchronized block/methodstate.
  • The main thread sleep 1500 milliseconds can just hit the thread 1 in the reenterstate and its print thread state, just that BLOCKEDstate.

These three looks a bit around, read it several times more than think about should be able to understand.

TERMINATED state

API Notes :

/**
 * Thread state for a terminated thread.
 * The thread has completed execution.
 */ 
TERMINATED;
复制代码

The end of the thread corresponding to the thread state where the thread has finished.

TERMINATEDState indicates that the thread is over. A thread instance can only be started once, precisely, will only be called once Thread#run()method, Thread#run()after the method of execution, the thread will be more changes to the state TERMINATED, meaning the thread life cycle has ended.

Here is a simple example:

public class ThreadState8 {

    public static void main(String[] args) throws Exception {
        Thread thread = new Thread(() -> {

        });
        thread.start();
        Thread.sleep(50);
        System.out.println(thread.getState());
    }
}
// 输出结果
TERMINATED
复制代码

Context switching

Multithreaded environment, when the state of a thread RUNNABLEinto a non RUNNABLE( BLOCKED, WAITINGor TIMED_WAITING), the respective thread context information (that is often said Context, including the contents of CPU registers and the program counter at the point in time, etc.) need is saved to the thread later restored to RUNNABLEbe able to continue on the basis of progress in the implementation of the previous state. The state of a thread by a non- RUNNABLEstate entry RUNNABLEmay relate to a previously saved state to restore the thread context information and proceed on this basis. Here the process thread context information to save and restore is called a context switch ( Context Switch).

Thread context switch will bring additional performance overhead, which includes saving and restoring thread context information overhead thread scheduling CPU time overhead and CPU cache content codes fail overhead (threads execute its access from the CPU cache variable than the value required from the main memory (RAM) in response to the value of the variable to be accessed faster, but the thread associated thread context switch can cause the accessed content invalidation CPU cache, the CPU generally L1 CacheandL2 Cache , so that the associated thread is slightly after being forced to reschedule its visit the main variables in memory to run again in order to re-create the CPU cache content).

In the Linux system, you can vmstatview the global context switching command, for example:

$ vmstat 1
复制代码

For running Java programs, can also in the Linux system perfto monitor command, for example:

$ perf stat -e cpu-clock,task-clock,cs,cache-reference,cache-misses java YourJavaClass
复制代码

Reference is mentioned under Windows system can bring their own tools perfmonto monitor (in fact, is the Task Manager) thread context switching, in fact, I did not find the task manager there is no way to switch the viewing context, after searching through discovery a tool: Process Explorer . Run Process Explorerrun a Java program and see its status:

Because playing a breakpoint, you can see the context switching programs running in a total of more than 7,000, the context of the current one-second increments switch 26 (because the author set Process Explorerto refresh the data once per second).

Monitoring thread status

If the project is running in a production environment, it is impossible to call frequently Thread#getState()change the method to monitor the state of the thread. JDK itself provides a number of tools to monitor the state of the thread, there are some open source tools such as lightweight Ali Arthas , briefly outline JDKsome of the tools comes.

Use jvisualvm

jvisualvmJDK comes with a heap of threads waiting JVM metrics monitoring tools, suitable for use in development and test environments. It is located JAVA_HOME/binunder the directory.

Which 线程Dumpis similar to the buttons mentioned below jstackcommand, used to derive the stack information for all threads.

Use jstack

jstackJDK comes with a command-line tool that is used to obtain the specified function PIDof the Java process thread stack information. For example, a locally running IDEAinstances PIDis 11376, you only need to enter:

jstack 11376
复制代码

In addition, if you want to locate a specific Java process PID, you can use the jpscommand.

Use JMC

JMCThat is Java Mission Control, it also comes with the JDK task monitoring tool that provides functionality than the jvisualvmpowerful, including MBean processing thread stack state has been viewed, the flight recorder and so on.

summary

Switching to understand Java thread state and some control measures, more conducive to the development of multi-threaded programs daily, a problem for the production environment, can quickly locate the problem through the stack information monitor thread root cause (Generally speaking, the more mainstream MVCapplications It is a request by a single thread processing, when the request occurs blocked, deriving a corresponding processing request to the blocking threads may be positioned substantially precise position, if for example the message queue RabbitMQ, the consumer thread jam similar idea may also be utilized solve).

References:

  • Jdk11 relevant source code
  • "Java multi-threaded programming practical guide"
  • "In-depth understanding of the Java Virtual Machine -2nd"

link

(This article End c-7-d ea-20190623 business iteration recently a little busy)

Guess you like

Origin juejin.im/post/5d922aa7e51d45782632e375