2

In Java, using the Thread class can create threads at the operating system level and bind them to the corresponding Thread class instance. Using threads to perform tasks asynchronously is the basis of concurrent programming. This article reads the Thread source code to understand the definition of thread status, related methods of thread scheduling, and the processing of thread interruptions.

This article is based on jdk1.8.0_91

1. Inheritance system

The thread class Thread implements the Runnable interface and has a Runnable attribute, which represents the task that needs to be executed by the thread.
The Thread#run method internally calls the run method of the property Runnable; if the Runnable property is empty, it does nothing.

/**
 * A <i>thread</i> is a thread of execution in a program. The Java
 * Virtual Machine allows an application to have multiple threads of
 * execution running concurrently.
 *
 * @author  unascribed
 * @see     Runnable
 * @see     Runtime#exit(int)
 * @see     #run()
 * @see     #stop()
 * @since   JDK1.0
 */
public class Thread implements Runnable {

    /* What will be run. */
    private Runnable target;
    
    /**
     * If this thread was constructed using a separate
     * <code>Runnable</code> run object, then that
     * <code>Runnable</code> object's <code>run</code> method is called;
     * otherwise, this method does nothing and returns.
     * <p>
     * Subclasses of <code>Thread</code> should override this method.
     *
     * @see     #start()
     * @see     #stop()
     * @see     #Thread(ThreadGroup, Runnable, String)
     */
    @Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }
}

If you execute Thread#run directly, it is just a normal call and will not start a new thread to perform the task.
The correct usage is to execute Thread#start to start a new thread, and the thread will call Thread#run to perform the task.

2. Thread creation

"Thread creation" is a vague concept. In some scenarios it refers to creating instances of the Thread class in memory, and in other scenarios it refers to creating native threads at the operating system level. In order to distinguish these two completely different situations, this article refers to creating an instance of the Thread class as "creating a thread", and creating an operating system native thread as a "starting thread".

2.1 Constructor

public Thread() {
    init(null, null, "Thread-" + nextThreadNum(), 0);
}
public Thread(String name) {
    init(null, null, name, 0);
}
public Thread(Runnable target) {
    init(null, target, "Thread-" + nextThreadNum(), 0);
}
public Thread(Runnable target, String name) {
    init(null, target, name, 0);
}
public Thread(ThreadGroup group, Runnable target) {
    init(group, target, "Thread-" + nextThreadNum(), 0);
}
public Thread(ThreadGroup group, String name) {
    init(group, null, name, 0);
}
public Thread(ThreadGroup group, Runnable target, String name) {
    init(group, target, name, 0);
}
public Thread(ThreadGroup group, Runnable target, String name, long stackSize) {
    init(group, target, name, stackSize);
}

In the end, the Thread#init method is called:

/**
 * Initializes a Thread with the current AccessControlContext.
 * @see #init(ThreadGroup,Runnable,String,long,AccessControlContext)
 */
private void init(ThreadGroup g, Runnable target, String name,
                  long stackSize) {
    init(g, target, name, stackSize, null);
}

/**
 * Initializes a Thread.
 *
 * @param g the Thread group
 * @param target the object whose run() method gets called
 * @param name the name of the new Thread
 * @param stackSize the desired stack size for the new thread, or
 *        zero to indicate that this parameter is to be ignored.
 * @param acc the AccessControlContext to inherit, or
 *            AccessController.getContext() if null
 */
private void init(ThreadGroup g, Runnable target, String name, long stackSize, AccessControlContext acc) {
    ...
}

Entry description:

  • ThreadGroup g (thread group)
  • Runnable target (Runnable object, representing the task that the thread needs to perform)
  • String name (the name of the thread)
  • long stackSize (the size of the stack allocated for the thread, if it is 0, this parameter is ignored)
  • AccessControlContext acc (the access control context inherited to the thread, the default is empty, which means to use AccessController.getContext())

2.2 Create a thread instance

In the official JDK documentation, two ways to instantiate the Thread class are introduced:

There are two ways to create a new thread of execution.
One is to declare a class to be a subclass of Thread. This subclass should override the run method of class Thread. An instance of the subclass can then be allocated and started.
The other way to create a thread is to declare a class that implements the Runnable interface. That class then implements the run method. An instance of the class can then be allocated, passed as an argument when creating Thread, and started.

Method 1: Inherit the Thread class

Write a subclass of Thread and override the run method of the Thread class. Create an instance of this subclass to start threads to perform tasks.

For example, a thread that calculates a prime number greater than a certain specified value can be written as:

class PrimeThread extends Thread {
    long minPrime;
    PrimeThread(long minPrime) {
        this.minPrime = minPrime;
    }

    public void run() {
        // compute primes larger than minPrime
        ...
    }
}

The following code creates and starts a thread:

PrimeThread p = new PrimeThread(143);
p.start();

Method 2: Implement the Runnable interface

Write the Runnable interface implementation class to implement the run method of the Runnable interface. When creating a Thread, the instance of the Runnable class is passed as a parameter.

class PrimeRun implements Runnable {
    long minPrime;
    PrimeRun(long minPrime) {
        this.minPrime = minPrime;
    }

    public void run() {
        // compute primes larger than minPrime
        ...
    }
}

The following code creates and starts a thread:

PrimeRun p = new PrimeRun(143);
new Thread(p).start();

3. Thread start

3.1 Start the system thread

Executing new java.lang.Thread().start() will create and start a native thread on the operating system.

  • The java.lang.Thread#start method calls the native start0 method internally. This method will be mapped to the system level by the JVM to create and start a thread, bind the thread to the java.lang.Thread object, and then the JVM will call the run method of the java.lang.Thread object.
  • The result of this operation is that there will be two concurrently executing threads, one is the current thread that initiated the Thread#start call, and the other is the newly created thread that will execute Thread#run.
  • Note that it is illegal to start a thread multiple times. Especially when the thread has finished executing, it cannot be restarted.

java.lang.Thread#start

/**
 * Causes this thread to begin execution; the Java Virtual Machine
 * calls the <code>run</code> method of this thread.
 * <p>
 * The result is that two threads are running concurrently: the
 * current thread (which returns from the call to the
 * <code>start</code> method) and the other thread (which executes its
 * <code>run</code> method).
 * <p>
 * It is never legal to start a thread more than once.
 * In particular, a thread may not be restarted once it has completed
 * execution.
 *
 * @exception  IllegalThreadStateException  if the thread was already
 *               started.
 * @see        #run()
 * @see        #stop()
 */
public synchronized void start() {
    /**
     * This method is not invoked for the main method thread or "system"
     * group threads created/set up by the VM. Any new functionality added
     * to this method in the future may have to also be added to the VM.
     *
     * A zero status value corresponds to state "NEW".
     */
    if (threadStatus != 0)
        throw new IllegalThreadStateException();

    /* Notify the group that this thread is about to be started
     * so that it can be added to the group's list of threads
     * and the group's unstarted count can be decremented. */
    group.add(this);

    boolean started = false;
    try {
        start0(); // 由 JVM 映射到系统层面,创建线程!
        started = true;
    } finally {
        try {
            if (!started) {
                group.threadStartFailed(this);
            }
        } catch (Throwable ignore) {
            /* do nothing. If start0 threw a Throwable then
              it will be passed up the call stack */
        }
    }
}

private native void start0();

3.2 JVM thread model

According to Chapter 12, Section 4 of "In-depth Understanding of the Java Virtual Machine", there are three main ways to implement threads:

  • Use kernel thread implementation (1:1 implementation);
  • Use user thread implementation (1:N implementation);
  • Use user thread plus lightweight process hybrid implementation (N:M implementation).

The HotSpot virtual machine adopts a 1:1 threading model. is directly mapped to an operating system native thread to achieve, and there is no additional indirect structure in between, so HotSpot itself will not interfere with the thread. Scheduled (you can set the thread priority to provide scheduling suggestions to the operating system), and leave it to the underlying operating system to handle it, so when to freeze or wake up the thread, how much processor execution time should be allocated to the thread, and which thread should be assigned to The execution of the processor core is all done by the operating system, and it is all determined by the operating system.
1:1线程模型

  • Light Weight Process (LWP) is a high-level interface of kernel threads, which is what we usually call threads. Programs generally do not use kernel threads directly, but use lightweight processes.
  • Kernel-Level Thread (KLT) is a thread directly supported by the operating system kernel (Kernel), and this thread is switched by the kernel.
  • The kernel (Kernel) schedules threads by manipulating the scheduler (Scheduler) and is responsible for mapping the tasks of the threads to each processor.

4. Thread status

4.1 State definition

In the java.lang.Thread class, the threadStatus attribute and State enumeration are defined to describe the state of the thread.

/* Java thread status for tools,
 * initialized to indicate thread 'not yet started'
 */
private volatile int threadStatus = 0;

/**
 * A thread state.
 * <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.
     *
     * 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;
}

4.2 Status description

By definition, a thread has 6 states:

  • NEW:A thread that has not yet started is in this state.
  • RUNNABLE:A thread executing in the Java virtual machine is in this state.
  • BLOCKED:A thread that is blocked waiting for a monitor lock is in this state.
  • WAITING:A thread that is waiting indefinitely for another thread to perform a particular action is in this state.
  • TIMED_WAITING:A thread that is waiting for another thread to perform an action for up to a specified waiting time is in this state.
  • TERMINATED:A thread that has exited is in this state.

A thread can only be in one state at the same time. These states are at the JVM level and will not be mapped to the thread state at the operating system level.

NEW

Threads that have not been started are in this state.

RUNNABLE

The thread running in the JVM is in this state.
The thread in this state is running in the JVM, but may need to wait for operating system resources, such as CPU scheduling.

BLOCKED

The thread waiting for the monitor lock is in this state.
Divided into two situations:

  1. Waiting to acquire the lock when entering the synchronized block for the first time;
  2. After calling the Object#wait and Object.wait methods, wait for the lock to be acquired when reentering the synchronized block.

WAITING

Threads waiting indefinitely are in this state.
Call one of the following methods, the thread will be in this state.

  • Object.wait
  • Thread.join
  • LockSupport.park

Threads in the waiting state are waiting for other threads to perform specific operations.

  • The thread that calls Object.wait waits for other threads to call Object.notify or Object.notifyAll
  • Thread calling Thread.join, waiting for other threads to terminate
  • The thread that calls LockSupport.park waits for other threads to call LockSupport.unpark

TIMED_WAITING

Threads waiting for a limited time are in this state.
Call one of the following methods (all parameters need to be passed in the timeout period), the thread will be in this state.

  • Thread.sleep
  • Object.wait(long)
  • Thread.join(long)
  • LockSupport.parkNanos
  • LockSupport.parkUntil

TERMINATED

Threads that have exited are in this state.
Indicates that the thread has finished running.

4.3 State transition

Thread State

4.4 Status classification

The 6 states of Java threads seem to be quite complicated, but in fact, BLOCKED, WATTING, and TIMED_WAITING all make the thread in a blocked state, so we classify these three categories as blocked state. Therefore, the Java thread life cycle can be simplified as the following figure:

Thread State

4.5 State method

In the java.lang.Thread class, the getState() method is defined to obtain the state of the thread, and the isAlive() method is defined to determine whether the current thread is active.

/**
 * Returns the state of this thread.
 * This method is designed for use in monitoring of the system state,
 * not for synchronization control.
 *
 * @return this thread's state.
 * @since 1.5
 */
public State getState() {
    // get current thread state
    return sun.misc.VM.toThreadState(threadStatus);
}

/**
 * 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();

The isAlive() method can be used to determine whether the current Thread class instance is bound to the system native thread.

@Test
public void state() throws InterruptedException {
    Thread thread = new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    });
    System.out.println("thread.isAlive() = " + thread.isAlive());  // false
    System.out.println("thread.getState() = " + thread.getState());// NEW

    thread.start();
    Thread.sleep(200);
    System.out.println("thread.isAlive() = " + thread.isAlive());  // true
    System.out.println("thread.getState() = " + thread.getState());// TIMED_WAITING

    thread.join();
    System.out.println("thread.isAlive() = " + thread.isAlive());  // false
    System.out.println("thread.getState() = " + thread.getState());// TERMINATED
}

5. Thread scheduling

The current thread calling Thread#sleep, Object#wait, Thread#join will enter the waiting state (WAITING/TIMED_WAITING).

5.1 Thread#sleep

  • Thread#sleep is a static native method.
  • The function is to make the current thread sleep for a period of time, the time unit is milliseconds. And no monitor lock will be released during hibernation.
  • After calling Thread#sleep, the current thread will enter the TIMED_WAITING state.
  • If other threads interrupt the current thread, the current thread is awakened from sleep, the interrupted state will be cleared, and InterruptedException will be thrown.
/**
 * 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;

There is another version of Thread#sleep that allows both millisecond and nanosecond values to be passed in at the same time, but the extra nanos will only make the thread sleep for 1 millisecond, which is not commonly used. Both Thread#join and Object#wait have similar methods that need to pass in millisecond and nanosecond values, so I won't repeat them in the following.

/**
 * Causes the currently executing thread to sleep (temporarily cease
 * execution) for the specified number of milliseconds plus the specified
 * number of nanoseconds, 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       // 毫秒
 *
 * @param  nanos
 *         {@code 0-999999} additional nanoseconds to sleep  // 纳秒
 *
 * @throws  IllegalArgumentException
 *          if the value of {@code millis} is negative, or the value of
 *          {@code nanos} is not in the range {@code 0-999999}
 *
 * @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 void sleep(long millis, int nanos)
throws InterruptedException {
    if (millis < 0) {
        throw new IllegalArgumentException("timeout value is negative");
    }

    if (nanos < 0 || nanos > 999999) {
        throw new IllegalArgumentException(
                            "nanosecond timeout value out of range");
    }

    if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
        millis++;
    }

    sleep(millis);
}

about Thread#sleep(0)

  • Thread.Sleep(0) does not really want the thread to suspend for 0 milliseconds. The meaning is that the current thread calling Thread.Sleep(0) is indeed frozen this time, allowing other threads to have the opportunity to execute first.
  • Thread.Sleep(0) is the current thread temporarily giving up the CPU, that is, releasing some unused time slices for use by other threads or processes, which is equivalent to a yielding action.

5.2 Object#wait

  • Object#wait is a non-static native method that needs to be used on object instances.
  • The function is to make the current thread wait for other threads to perform a specific operation, the time unit is milliseconds.
  • Before calling the Object#wait method, the thread must hold a monitor lock.
  • While the thread is waiting, it will release the lock and enter the TIMED_WAITING or WAITING state.
  • The thread is awakened from waiting, waits to acquire the lock again, and enters the BLOCKED state.
/**
 * Causes the current thread to wait until another thread invokes the
 * {@link java.lang.Object#notify()} method or the
 * {@link java.lang.Object#notifyAll()} method for this object, or
 * some other thread interrupts the current thread, or a certain
 * amount of real time has elapsed.
 */
public final native void wait(long timeout) throws InterruptedException;

/**
 * Causes the current thread to wait until another thread invokes the 
 * {@link java.lang.Object#notify()} method or the
 * {@link java.lang.Object#notifyAll()} method for this object.
 * In other words, this method behaves exactly as if it simply
 * performs the call {@code wait(0)}.
 */ 
public final void wait() throws InterruptedException {
    wait(0);
}

Example usage:

synchronized (obj) {
    while (<condition does not hold>) {
        obj.wait();
    }
    ... // Perform action appropriate to condition
}

About Object#wait(0)

  • Object#wait() actually calls Object#wait(timeout) internally, and the timeout period is 0 milliseconds, which means there is no timeout period.
  • Object#wait(0) is awakened: other threads call Object#notify or Object#notifyAll, or the current thread is interrupted, or spurious wakeup occurs.
  • Object#wait(timeout) Condition for being awakened: Object#notify or Object#notifyAll is called by other threads, or the current thread is interrupted, or false wake-up occurs, or waiting timeout.

5.3 Thread#join

  • Thread#join is a non-static and non-native method that needs to be used on thread instances.
  • The function is to make the current thread wait for the completion of the specified thread execution, the time unit is milliseconds.
/**
 * Waits for this thread to die.
 */
public final void join() throws InterruptedException {
    join(0);
}

/**
 * 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;
        }
    }
}

Calling Thread#join involves the interaction between two threads.

Such as thread threadA during operation, met threadB.join() this line of code, represents threadA need to enter to wait until threadB execution is completed.

Analyze the whole process:

  1. threadA executes the code threadB.join() . Because the Thread#join method is non-static and modified by synchronized, threadA first needs to acquire the lock of threadB.
  2. If threadA cannot acquire the lock, it will enter the blocking state BLOCKED; if it can acquire the lock, it will enter the method.
  3. After the time parameter verification is passed, Thread#isAlive will be executed to check whether the thread is alive. Note that this.isAlive() here is a method of the threadB object, so threadA is used to check whether threadB is alive.
  4. If threadB is still alive, threadA executes threadB.wait() to release the lock and enter the indefinite waiting state WAITING.
  5. When threadB finishes running and enters the TERMINATED state, the threadB.notifyAll() method will be called to wake up the threads waiting threadB.wait()
  6. When threadA is awakened from the waiting state WAITING, the threadB object lock is reacquired, and the threadB thread is cyclically checked whether it has survived. If it does not survive, the waiting is ended.

It is stated in the JDK source code comments that when the thread terminates, the Object#notifyAll method will be called.

As a thread terminates the {@code this.notifyAll} method is invoked.

Sample code:

@Test
public void join() throws InterruptedException {
    Thread threadB = new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                System.out.println(Thread.currentThread().getName() + " 开始运行...");
                Thread.sleep(1000);
                System.out.println(Thread.currentThread().getName() + " 结束运行...");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }, "threadB");
    Thread threadA = new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                System.out.println(Thread.currentThread().getName() + " 开始运行...");
                threadB.start();
                threadB.join();
                System.out.println(Thread.currentThread().getName() + " 结束运行...");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }, "threadA");
    threadA.start();
    threadA.join();
}

Results of the:

threadA 开始运行...
threadB 开始运行...
threadB 结束运行...
threadA 结束运行...

about Thread#join(0)

Thread#join() actually calls Thread#join(timeout) internally, and the timeout period is 0 milliseconds, which means there is no timeout period.

5.4 Thread#yield

The thread calls Thread#yield and does not enter the waiting state.

  • Thread#yield is a static native method.
  • The role is to make the current thread give up the CPU. But the CPU is just a suggestion. It is possible that a thread just gave up the CPU and then immediately acquired the CPU.
  • In contrast, the Thread#sleep method must give up CPU resources and sleep for a specified time without participating in CPU competition.
  • After executing Thread#yield, the thread is still in RUNNABLE state, and after executing Thread#sleep, the thread will change from RUNNABLE to TIMED_WAITING state.
/**
 * 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();

6. Interrupt

Interrupting a thread means stopping everything it is doing before the thread finishes its task, effectively suspending its current operation. However, there is no language requirement that an interrupted thread should terminate. Interrupting a thread is just to attract the attention of the thread, and the interrupted thread can decide how to respond to the interruption.

In addition, although Thread.stop does stop a running thread, this method is unsafe and may produce unpredictable results. It has been marked as an obsolete method in the JDK.

6.1 Interrupt status

In Java, every thread has an interrupt flag bit, which is used to indicate the interrupt status of the thread: interrupted and uninterrupted.

Methods of checking, clearing and setting interrupt flags are provided in java.lang.Thread.

6.2 Check interrupt

/**
 * 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);

java.lang.Thread#isInterrupted(boolean) is a native method used to check whether the thread has been interrupted, and the input parameter ClearInterrupted is used to control whether to clear the interrupted state.

The native method is called with the following two:

Thread#isInterrupted checks the interrupt status of the specified thread and does not clear the interrupt status of the thread.

/**
 * Tests whether this thread has been interrupted.  The <i>interrupted
 * status</i> of the thread is unaffected by this method.
 *
 * <p>A thread interruption ignored because a thread was not alive
 * at the time of the interrupt will be reflected by this method
 * returning false.
 *
 * @return  <code>true</code> if this thread has been interrupted;
 *          <code>false</code> otherwise.
 * @see     #interrupted()
 * @revised 6.0
 */
public boolean isInterrupted() {
    return isInterrupted(false);
}

Thread#interrupted checks the interrupt status of the current thread and clears the interrupt status of the current thread.

/**
 * Tests whether the current thread has been interrupted.  The 
 * <i>interrupted status</i> of the thread is cleared by this method.  In
 * other words, if this method were to be called twice in succession, the
 * second call would return false (unless the current thread were
 * interrupted again, after the first call had cleared its interrupted
 * status and before the second call had examined it).
 *
 * <p>A thread interruption ignored because a thread was not alive
 * at the time of the interrupt will be reflected by this method
 * returning false.
 *
 * @return  <code>true</code> if the current thread has been interrupted;
 *          <code>false</code> otherwise.
 * @see #isInterrupted()
 * @revised 6.0
 */
public static boolean interrupted() {
    return currentThread().isInterrupted(true);
}

6.2 Initiate an interrupt

  • Set the thread's interruption status to interrupted.
  • If the thread is calling the wait(), wait(long) or wait(long, int) method of the Object class, or calling the join(), join(long), join(long, int), sleep(long) or When the sleep(long, int) method is blocked, its interrupted status will be cleared, and it will also receive an InterruptedException.
  • Interrupting a thread that has terminated has no effect.

In other words, threadA executes threadB.interrupt() , threadB will have one of the following two results (depending on threadB itself):

  • The interruption status of threadB is uninterrupted, and InterruptedException is thrown;
  • The interrupt status of threadB is interrupted and no exception is thrown.

java.lang.Thread#interrupt

/**
 * Interrupts this thread.
 *
 * @throws  SecurityException
 *          if the current thread cannot modify this thread
 *
 * @revised 6.0
 * @spec JSR-51
 */
public void interrupt() {
    if (this != Thread.currentThread())
        checkAccess();

    synchronized (blockerLock) {
        Interruptible b = blocker;
        if (b != null) {
            interrupt0();           // Just to set the interrupt flag
            b.interrupt(this);
            return;
        }
    }
    interrupt0();
}

private native void interrupt0();

6.3 How to handle interrupts correctly

It is known that when a thread calls Object#wait, Thread#join, and Thread#sleep to enter the waiting state, it will be awakened by other threads calling Thread#interrupt.
At this time, the interrupted status of the current thread will be cleared, and an InterruptedException will be thrown.
If the current thread is unable to deliver InterruptedException for some reason, it is best to call interrupt again to restore the interrupted state for the upper caller to handle.

Error handling method:

void func() {
    try {
        Thread.sleep(50);
    } catch (InterruptedException e) {
        // nothing
    }
}

The correct way to deal with it:

void func() throw InterruptedException {
    Thread.sleep(50);
}

or

void func() {
    try {
        Thread.sleep(50);
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    }
}

6.4 Do not check for interrupts in the loop

If sleep is called in the loop, don't check the interrupt status unless the interrupt exception is handled correctly.

Error handling method:

@Test
public void dealInterrupt() {
    Thread subThread = new Thread(new Runnable() {
        @Override
        public void run() {
            int i = 0;
            while (!Thread.currentThread().isInterrupted()) { // 循环检查中断状态
                try {
                    System.out.println(Thread.currentThread().getName() + " 开始第【" + i + "】休眠...");
                    Thread.sleep(1000);
                    System.out.println(Thread.currentThread().getName() + " 结束第【" + i + "】休眠...");
                    ++i;
                } catch (InterruptedException e) { // 如果调用sleep受阻,会抛出异常,同时中断状态true将被清除为false
                    System.out.println(Thread.currentThread().getName() + " " + e.getMessage());
                    // 只要正确地处理中断,也可以让循环停止。
                    // Thread.currentThread().interrupt();
                }
            }
        }
    });
    subThread.start();

    // 主线程执行一段时间,中断子线程,再继续观察子线程一段时间
    try {
        Thread.sleep(1000);
        subThread.interrupt();
        Thread.sleep(5000);
    } catch (InterruptedException e) {
        System.out.println(Thread.currentThread().getName() + " " + e.getMessage());
    }
}

This example will have two execution results:

If the child thread is interrupted during sleep, the interrupt state will be cleared, causing the child thread to loop indefinitely.

Thread-0 开始第【0】休眠...
Thread-0 sleep interrupted
Thread-0 开始第【0】休眠...
Thread-0 结束第【0】休眠...
Thread-0 开始第【1】休眠...
Thread-0 结束第【1】休眠...
Thread-0 开始第【2】休眠...
Thread-0 结束第【2】休眠...
Thread-0 开始第【3】休眠...
Thread-0 结束第【3】休眠...
Thread-0 开始第【4】休眠...
Thread-0 结束第【4】休眠...
Thread-0 开始第【5】休眠...

If the child thread is interrupted after sleep, the child thread can be terminated because of the interrupt state.

Thread-0 开始第【0】休眠...
Thread-0 结束第【0】休眠...

The correct way to deal with it:

@Test
public void dealInterrupt02() {
    Thread subThread = new Thread(new Runnable() {
        @Override
        public void run() {
            int i = 0;
            boolean isLoop = true;
            while (isLoop) { // 使用自定的循环控制标识
                try {
                    System.out.println(Thread.currentThread().getName() + " 开始第【" + i + "】休眠...");
                    Thread.sleep(1000);
                    System.out.println(Thread.currentThread().getName() + " 结束第【" + i + "】休眠...");
                    ++i;
                } catch (InterruptedException e) {
                    System.out.println(Thread.currentThread().getName() + " " + e.getMessage());
                    isLoop = false;
                }
            }
        }
    });
    subThread.start();

    // 主线程执行一段时间,中断子线程,再继续观察子线程一段时间
    try {
        Thread.sleep(1000);
        subThread.interrupt();
        Thread.sleep(5000);
    } catch (InterruptedException e) {
        System.out.println(Thread.currentThread().getName() + " " + e.getMessage());
    }
}

6.5 How to stop the thread correctly

  1. Tasks generally have a loop structure, as long as you use a mark to control the loop, you can end the task.
  2. If the thread is in the waiting state and cannot read the mark, the interrupt() method can be used to force the thread to resume from the waiting state to the running state, so that the thread has the execution qualification of the CPU, and then decides whether it should terminate.

7. Summary

  1. Creating a thread has two different meanings. new Thread() will create an instance of the Thread class in memory, while new Thread().start() will create and start the native thread of the operating system.
  2. JVM uses a 1:1 threading model, and each Java thread is directly mapped to a native thread of the operating system.
  3. There are six thread states defined in the Thread class, which are not mapped to the thread states at the operating system level.
  4. Thread#sleep, Object#wait, Thread#join, Thread#yield and LockSupport related methods can all be used to schedule threads.
  5. The thread has an interrupt state, and the interrupt should be used to terminate the operation of a thread. When the current thread is interrupted, the interruption needs to be handled correctly.

Author: Sumkor
Link: https://segmentfault.com/a/1190000039998094


Sumkor
148 声望1.3k 粉丝

会写点代码