1. Thread status in the operating system

The thread states in operating system have three key states: running, ready and waiting

  • Ready: A thread is waiting to use the CPU and can enter the running state when called by the scheduler
  • Running: The thread is using the CPU
  • Waiting: a thread that has been called for a waiting event or is waiting for another resource (such as I/O)

Q Why are threads in the operating system not suspended?

A Since threads are not the ownership units of resources, the suspended state is meaningless to threads. If A process is swapped out of main memory after suspension, all threads must also swap out because they share the address space of the process. You can see that the state caused by the suspend operation is process-level state and not considered thread-level state. Similarly, the termination of a process results in the termination of all threads in the process.

Six thread states in Java

Thread states in Java have six thread states.

Thread. State the source code

public enum State {
  
        NEW,

        RUNNABLE,

        BLOCKED,

        WAITING,

        TIMED_WAITING,

        TERMINATED;
    }
Copy the code

NEW Threads that have not been started are in this state. RUNNABLE The thread executing in the Java virtual machine is in this state. BLOCKED is the state in which a thread is BLOCKED while waiting for a monitor lock. WAITING A thread in this state that is WAITING indefinitely for another thread to perform a particular operation. TIMED_WAITING A thread waiting for another thread to perform an operation has at most reached the specified waiting time. The thread is in this state. The TERMINATED thread is in this state. A thread can only be in one state at a given point in time. These states are virtual machine states and do not reflect any operating system thread states.

Q Is it ok to call the start() method twice on the same thread?

After A call to start(), the value of threadStatus changes (threadStatus! = 0), again call start () method throws IllegalThreadStateException anomalies.

Take a look at the source code for the start() method

    public synchronized void start(a) {

        if(threadStatus ! =0)
            throw new IllegalThreadStateException();

        group.add(this);

        boolean started = false;
        try {
            // This is a native method
            start0();
            started = true;
        } finally {
            try {
                if(! started) { group.threadStartFailed(this); }}catch (Throwable ignore) {

            }
        }
    }
Copy the code

ThreadStatus after new is 0, but after calling native start0(), what state will it change to? Let’s debug it together.

package co.dianjiu.thread;

public class MyThreadStatus extends Thread{
    @Override
    public void run(a){
        System.out.println("MyThreadStatus");
    }

    public static void main(String[] args) {
        MyThreadStatus myThreadStatus = newMyThreadStatus(); System.out.println(myThreadStatus.getState()); myThreadStatus.start(); System.out.println(myThreadStatus.getState()); myThreadStatus.start(); System.out.println(myThreadStatus.getState()); }}Copy the code

NEW MyThreadStatus RUNNABLE Exception in thread “main” java.lang.IllegalThreadStateException at java.base/java.lang.Thread.start(Thread.java:789) at co.dianjiu.thread.MyThreadStatus.main(MyThreadStatus.java:14)

Take a look at the source code for the getState() method

public State getState(a) {
        // get current thread state
        return jdk.internal.misc.VM.toThreadState(threadStatus);
    }
Copy the code

ThreadStatus breakpoints after new come in with a value of 0

The threadStatus breakpoint after start comes in with a value of 5

Thread state transitions in Java

3.1. Transition between blocking state and ready state

Threads are BLOCKED because they are waiting for locks to be released. If there are two threads A and B, thread A has acquired the lock early and has not released it yet, thread B is BLOCKED. Let’s start with an example:

package co.dianjiu.thread;

public class MyThreadBlocked extends Thread{
    @Override
    public void run(a){
        testMethod();
    }

    public static void main(String[] args) throws InterruptedException {
        MyThreadBlocked a = new MyThreadBlocked();
        a.setName("a");
        MyThreadBlocked b = new MyThreadBlocked();
        b.setName("b");
        a.start();
        // Note that the main thread sleeps for 1000 milliseconds and the testMethod() thread sleeps for 2000 milliseconds
        Thread.sleep(1000L);
        b.start();
        System.out.println(a.getName() + ":" + a.getState());
        System.out.println(b.getName() + ":" + b.getState());
    }

    // Synchronized methods compete for locks
    private static synchronized void testMethod(a) {
        try {
            Thread.sleep(2000L);
            System.out.println(Thread.currentThread().getName()+"= = = = >"+Thread.currentThread().getState());
        } catch(InterruptedException e) { e.printStackTrace(); }}}Copy the code

The execution result

a:TIMED_WAITING

b:BLOCKED

a====>RUNNABLE

b====>RUNNABLE

3.2 Transition between waiting state and ready state

As you can see from the transition diagram above, there are three ways to transition from the wait state to the ready state. Let’s take a look at the specific use case and the source code implementation for each method.

Object.wait()

The thread must hold the lock on the object before calling wait().

When a thread calls wait(), it releases the current lock until another thread calls notify()/notifyAll() to wake up the thread.

package co.dianjiu.thread;

public class MyThreadWaiting extends Thread{
    private static final Object lock = new Object();

    public static void main(String[] args) throws InterruptedException {

        Thread a = new Thread(() -> {
            System.out.println("Thread A is waiting to acquire A lock lock");
            synchronized (lock) {
                try {
                    System.out.println("Thread A has acquired the lock lock and will run the lock.wait() method to wait.");
                    lock.wait();
                } catch(InterruptedException e) { e.printStackTrace(); }}}); a.setName("a");
        Thread b = new Thread(() -> {
            System.out.println("Thread B is waiting to acquire the lock");
            System.out.println(Thread.currentThread().getName() + "= = = >" + Thread.currentThread().getState());
            synchronized (lock) {
                System.out.println("Thread B is waiting to acquire the lock lock and will run the lock.notify() method.");
                lock.notify();
                //lock.notifyAll();
                System.out.println(a.getName() + "= = = >"+ Thread.currentThread().getState()); }}); b.setName("b");
        System.out.println(a.getName() + "= = = >" + a.getState());
        System.out.println(b.getName() + "= = = >" + b.getState());
        a.start();
        System.out.println(a.getName() + "= = = >" + a.getState());
        b.start();
        System.out.println(a.getName() + "= = = >"+ a.getState()); }}Copy the code

The execution result

A ===>NEW B ===>NEW Thread A is WAITING for the lock lock and will run lock.wait() on thread A ===>RUNNABLE Thread B waits to acquire the lock lock and will run the lock.notify() method a===>RUNNABLE

Wait () = wait(Long) = 0

public final void wait(a) throws InterruptedException {
        wait(0L);
    }
Copy the code

The wake up method calls the Java native Interface (JNI)

Other threads calling notify() will only wake up a single thread waiting for the lock. Multiple threads waiting for the lock will not necessarily wake up the thread that called wait(). Similarly, a notifyAll() call to wake up all threads waiting for a lock does not necessarily immediately allocate the time slice to the thread that just gave up the lock, depending on the system schedule.

    @HotSpotIntrinsicCandidate
    public final native void notify(a);

    @HotSpotIntrinsicCandidate
    public final native void notifyAll(a);
Copy the code

Thread.join()

Calling the join() method does not release the lock. It waits until the current thread is TERMINATED.

package co.dianjiu.thread;

public class MyThreadJoin extends Thread{
    @Override
    public void run(a){
        System.out.println(Thread.currentThread().getName() + "= = = >" + Thread.currentThread().getState());
    }
    public static void main(String[] args) throws InterruptedException {
        MyThreadBlocked a = new MyThreadBlocked();
        a.setName("a");
        MyThreadBlocked b = new MyThreadBlocked();
        b.setName("b");
        System.out.println(a.getName() + "= = = >" + a.getState());
        System.out.println(b.getName() + "= = = >" + b.getState());
        a.start();
        a.join();
        b.start();
        System.out.println(a.getName() + "= = = >" + a.getState());
        System.out.println(b.getName() + "= = = >"+ b.getState()); }}Copy the code

The execution result

a===>NEW

b===>NEW

a====>RUNNABLE

a===>TERMINATED

b===>TIMED_WAITING

b====>RUNNABLE

Join () = join(Long) = 0

public final void join(a) throws InterruptedException {
        join(0);
    }
Copy the code

LockSupport.park()

Disable the current thread at thread scheduling time and place it on the WaitSet queue. Unless you have a permit. A license is the 0-1 mark mentioned above. Using unpark generates a license. If the license is available, it will be consumed and the call will return immediately; Otherwise, for thread scheduling purposes, the current thread will be disabled and put to sleep until one of the following three conditions occurs:

  • Other threads call the unpark method targeting the current thread.
  • Another thread interrupts the current thread.
  • A false return was called.
package co.dianjiu.thread;

import java.util.concurrent.locks.LockSupport;

public class MyThreadPark {
    public static void main(String[] args) throws InterruptedException {

        Thread a = new Thread(() -> {
            System.out.println("Thread A has acquired the lock lock and will run the locksupport.park () method to wait.");
            LockSupport.park(Thread.currentThread());
        });
        a.setName("a");
        Thread b = new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "= = = >" + Thread.currentThread().getState());
            System.out.println("Thread B is waiting to acquire the lock lock and will run the locksupport.unpark () method.");
            LockSupport.unpark(a);
            //a.interrupt();
            System.out.println(a.getName() + "= = = >" + Thread.currentThread().getState());
        });
        b.setName("b");
        System.out.println(a.getName() + "= = = >" + a.getState());
        System.out.println(b.getName() + "= = = >" + b.getState());
        a.start();
        System.out.println(a.getName() + "= = = >" + a.getState());
        b.start();
        System.out.println(a.getName() + "= = = >"+ a.getState()); }}Copy the code

A ===>NEW b===>NEW thread A has a lock lock and will run locksupport.park () to wait a===>RUNNABLE a===>WAITING b===>RUNNABLE Thread B waits to acquire the lock lock and will run the locksupport.unpark () method a===>RUNNABLE

3.3. Timeout wait and ready state transition

Thread.sleep(long)

Causes the current thread to sleep for the specified time. Note that sleep only temporarily stops the thread from executing and does not release the lock. When the time is up, the thread re-enters the RUNNABLE state.

Object.wait(long)

The wait(long) method puts the thread into a TIMED_WAITING state. Wait (long) is the same as wait() in that it can be woken up by another thread calling notify() or notifyAll().

The difference is that the wait(long) method wakes up after a specified time, even if no other thread wakes it up.

Thread.join(long)

Join (long) causes the current thread to execute at a specified time and to enter a TIMED_WAITING state.

Let’s change the example again:

package co.dianjiu.thread;

public class MyThreadJoinTime extends Thread{
    @Override
    public void run(a){
        System.out.println(Thread.currentThread().getName() + "= = = >" + Thread.currentThread().getState());
    }
    public static void main(String[] args) throws InterruptedException {
        MyThreadBlocked a = new MyThreadBlocked();
        a.setName("a");
        MyThreadBlocked b = new MyThreadBlocked();
        b.setName("b");
        System.out.println(a.getName() + "= = = >" + a.getState());
        System.out.println(b.getName() + "= = = >" + b.getState());
        a.start();
        a.join(1000L);
        b.start();
        System.out.println(a.getName() + "= = = >" + a.getState());
        System.out.println(b.getName() + "= = = >"+ b.getState()); }}Copy the code

The execution result

a===>NEW

b===>NEW

a===>TIMED_WAITING

b===>BLOCKED

a====>RUNNABLE

b====>RUNNABLE

Since the execution time of thread A is specified, and the execution time is less than the sleep time of thread A, the state of thread A output TIMED_WAITING. B The thread state remains unfixed (RUNNABLE or BLOCKED).

3.4. Thread interruption status

In some cases, we need to interrupt the thread after it has been started when it is not needed to continue. There is currently no safe and direct way to stop a thread in Java, but Java provides a thread interrupt mechanism to handle the need to interrupt a thread.

The thread interrupt mechanism is a cooperative mechanism. Note that interrupts do not terminate a thread directly, but tell the thread that needs to be interrupted to do so.

The Thread class provides several methods for Thread interrupts:

  • Thread.interrupt() : Interrupts a Thread. Instead of stopping the thread immediately, the thread’s interrupt status is set to true (flase by default);
  • Thread.interrupted() : Tests whether the current Thread is interrupted. The interrupted status of a thread is affected by this method, meaning that a single call sets the interrupted status of the thread to true, and two consecutive calls return the interrupted status of the thread to false.
  • Thread.isinterrupted () : tests whether the current Thread isInterrupted. Unlike the above method, calling this method does not affect the interrupted state of the thread.

In the interrupt mechanism, the interrupt status of the thread is set to true after another thread informs the thread that needs to be interrupted. However, it is entirely up to the interrupted thread to decide what to do with the interrupt request. It can either process the interrupt request at the appropriate time or continue without processing the interrupt request at all.