(Mobile phone landscape view source more convenient)


Note: The Java source analysis section is based on the Java 8 version unless otherwise noted.

Introduction to the

Everyone knows threads have a life cycle, but Tongren can seriously tell you that almost no article on the Internet gets it completely right.

Common errors include: ready state, RUNNING state, dead state, interrupted state, blocked without wait state, flowchart scribble, etc. The most common error is that a thread has only 5 states.

Today’s article will thoroughly explain the thread life cycle and analyze the logic of thread state changes in synchronized, AQS-based locks.

So, synchronized lock and AQS principle (source) do not understand the students, please read tongge before the article familiar with the content of these two parts, otherwise certainly can not remember the thread life cycle here.

The problem

(1) What are the states of threads?

(2) What is the status of the thread at each stage of synchronized lock?

(3) What is the state of the thread at each stage of reentrant lock and conditional lock?

On the first source

For the life cycle of a Thread, we can look at the java.lang.Thread.State class, which is an internal enumeration of threads, defines the various states of a Thread and is clearly commented on.


public enum State {
    /** * The thread has not started yet */
    NEW,

    /** * Runnable, running or waiting for system resources, such as CPU */
    RUNNABLE,

    /** * BLOCKED after waiting for a monitor lock (synchronized) * or after calling object.wait () and being notified () */
    BLOCKED,

    * 1.object.wait () after no timeout method and before notifying (), If notified (), the virtual BLOCKED state is BLOCKED. * 2. Thread.join() after the method that does not timeout * 3
    WAITING,

    Thread.sleep(); thread.sleep (); object.wait (timeout); If a timeout is reached or notified (), the virtual drive is BLOCKED * 3. After thread. join(timeout) * 4. Locksupport. parkUntil(deadline) method after */
    TIMED_WAITING,

    /** * terminates, the thread has finished executing */
    TERMINATED;
}
Copy the code

The flow chart

Now that we have commented out the states in the thread lifecycle, let’s look at the flow between states:

How’s that? Isn’t it complicated? Tong Elder brother has almost checked all the information on the Internet, and there is no article to draw this flow chart completely. Tong Elder brother will explain one by one below:

(1) For the sake of explanation, we divide locks into two categories: synchronized locks and AQS based locks (we take reentrants as examples), i.e. locks that use locksupport-park ()/parkNanos()/parkUntil() internally;

(2) Both synchronized locks and AQs-based locks are internally divided into two queues, one is synchronization queue (AQS queue) and the other is waiting queue (Condition queue);

(3) If object.wait()/wait(timeout) or condition.await()/await(timeout) methods are called internally, the thread will enter the wait queue first and be notified ()/signal() or timeout before entering the synchronization queue;

(4) Explicitly declare that BLOCKED is only available when a thread is in a synchronized queue, and nothing else is relevant to this state;

(5) Synchronized: when synchronized is executed, if the lock is immediately acquired (it does not enter the synchronization queue), the thread is in the RUNNABLE state;

(6) Synchronized: when synchronized is executed, the thread is BLOCKED if it cannot obtain the lock (directly entering the synchronization queue);

(5) Synchronized, the thread is in WAITING state after calling object.wait();

(6) Synchronized, after calling object.wait(timeout), the thread is in TIMED_WAITING state (entering the waiting queue);

(7) Synchronized, after calling Object.wait () and notifying (), if the lock is immediately acquired (i.e., the thread does not enter the synchronization queue), the thread is in the RUNNABLE state;

(8) Synchronized internal, after calling object.wait(timeout) and notifying (), if the thread immediately obtains the lock (i.e., does not enter the synchronization queue), the thread is in the RUNNABLE state;

(9) Synchronized internal, after calling object.wait(timeout) and timed out, if the thread immediately acquired the lock (that is, did not enter the synchronization queue), the thread was in the RUNNABLE state;

(10) Synchronized internal, after calling object.wait() and being notified (), the thread is BLOCKED if it cannot acquire the lock (that is, it enters the synchronization queue);

(11) Synchronized, after calling Object.wait (timeout) and notify() or timeout, if the thread cannot obtain the lock (that is, enter the synchronization queue), the thread is in the BLOCKED state;

(12) For a reentrant lock, a thread executes lock.lock() and is in a RUNNABLE state if the lock is immediately acquired.

(13) The thread is in a WAITING state when it executes lock.lock() and fails to obtain the lock.

(14) For the interior of the reentrant lock, the thread is in WAITING after condition.await() is called;

(15) For the interior of the reentrant lock, the thread is in TIMED_WAITING after condition.await(timeout) is called;

(16) For the interior of a reentrant lock, the thread is in a RUNNABLE state if it immediately acquires the lock after condition.await() has been called and is signalled ();

(17) For the interior of a reentrant lock, the thread is in a RUNNABLE state if the lock is immediately acquired after condition.await(timeout) has been called and is signalled ();

(18) Within a reentrant lock, the thread is in a RUNNABLE state if it immediately acquires the lock (i.e. does not enter the synchronization queue) after calling condition. Await (timeout).

(19) For a reentrant lock, the thread is WAITING if it cannot obtain the lock after condition.await() has been called and is signalled ();

(20) For the interior of a reentrant lock, if the thread cannot obtain the lock after calling condition.await(timeout) and is signaled () or has timed out, the thread is in a WAITING state;

(21) For a reentrant lock, if the lock cannot be obtained after an internal call to condition.await() and after being signalled (), the lock will actually undergo two WAITING state switches, one in the WAITING queue and one in the synchronization queue.

(22) For reentrant locks, if the condition. Await (timeout) is called internally and is signaled () or timed out, there will be a state transition from TIMED_WAITING to WAITING, i.e. from WAITING queue to synchronous queue;

In order to facilitate understanding, tong elder brother here each point is relatively fine, trouble patience to finish reading.

The test case

After reading the above section, you must want to know how to verify. Tong Ge will talk about the verification method, first give the test case.

public class ThreadLifeTest {
    public static void main(String[] args) throws IOException {
        Object object = new Object();
        ReentrantLock lock = new ReentrantLock();
        Condition condition = lock.newCondition();

        new Thread(()->{
            synchronized (object) {
                try {
                    System.out.println("thread1 waiting");
                    object.wait();
// object.wait(5000);
                    System.out.println("thread1 after waiting");
                } catch(InterruptedException e) { e.printStackTrace(); }}},"Thread1").start();

        new Thread(()->{
            synchronized (object) {
                try {
                    System.out.println("thread2 notify");
                    // Open or close this comment and observe the state of Thread1
// object.notify(); [This article by the public from the number "Tong Elder brother read source code" original]
                    After notify, the current thread does not release the lock, but the notify thread enters the synchronization queue from the wait queue
                    // Sleep does not release the lock
                    Thread.sleep(10000000);
                } catch(InterruptedException e) { e.printStackTrace(); }}},"Thread2").start();

        new Thread(()->{
            lock.lock();
            System.out.println("thread3 waiting");
            try {
                condition.await();
// condition.await(200, (TimeUnit).SECONDS);
                System.out.println("thread3 after waiting");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally{ lock.unlock(); }},"Thread3").start();

        new Thread(()->{
            lock.lock();
            System.out.println("thread4");
            // Open or close this comment and observe the state of Thread3
// condition.signal(); [This article by the public from the number "Tong Elder brother read source code" original]
            // After signal, the current thread does not release the lock, but only enters the synchronization queue from the wait queue
            // Sleep does not release the lock
            try {
                Thread.sleep(1000000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally{ lock.unlock(); }},"Thread4").start(); }}Copy the code

Turn on or off the code in the comments above, RUN the code using IDEA’s RUN mode, and then click one of the camera buttons on the left (jStack) to see the status of each thread.

Note: Do not use DEBUG mode, all DEBUG modes are WAITING.

eggs

The thread pool life cycle is a thread pool life cycle. The thread pool life cycle is a thread pool life cycle.


Welcome to pay attention to my public number “Tong Elder brother read source code”, view more source code series articles, with Tong elder brother tour the ocean of source code.