关于java:阅读-JDK-源码线程类-Thread

38次阅读

共计 21863 个字符,预计需要花费 55 分钟才能阅读完成。

在 Java 中,应用 Thread 类能够在操作系统层面创立线程,并绑定到对应的 Thread 类实例中。利用线程异步地执行工作,是并发编程的根底。本文通过浏览 Thread 源码,理解线程状态的定义,线程调度的相干办法,以及对线程中断的解决等。

本文基于 jdk1.8.0_91

1. 继承体系

线程类 Thread 实现了 Runnable 接口,并且具备一个 Runnable 属性,示意须要线程执行的工作。
Thread#run 办法外部调用了属性 Runnable 的 run 办法;若 Runnable 属性为空则什么也不做。

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

如果间接执行 Thread#run 只是一个一般的调用,并不会启动新的线程来执行工作。
正确是用法是执行 Thread#start 来启动新的线程,由该线程来调用 Thread#run 执行工作。

2. 线程的创立

“创立线程”是一个含糊的概念,某些场景下指的是在内存中创立 Thread 类的实例,在另一些场景下指的是在操作系统层面创立原生线程。为了辨别这两种截然不同的状况,本文将创立 Thread 类的实例称为“创立线程”,将创立操作系统原生线程称为“启动线程”。

2.1 构造函数

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);
}

最终都是调用 Thread#init 办法:

/**
 * 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) {...}

入参阐明:

  • ThreadGroup g(线程组)
  • Runnable target(Runnable 对象,示意线程需执行的工作)
  • String name(线程的名字)
  • long stackSize(为线程调配的栈的大小,若为 0 则示意疏忽这个参数)
  • AccessControlContext acc(继承给线程的拜访权限管制上下文,默认为空,示意应用 AccessController.getContext())

2.2 创立线程实例

在 JDK 官网文档中,介绍了 Thread 类实例化的两种形式:

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.

形式一:继承 Thread 类

编写 Thread 的子类,重写 Thread 类的 run 办法。创立该子类的实例以启动线程执行工作。

例如,计算大于某一规定值的质数的线程能够写成:

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

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

下列代码会创立并启动一个线程:

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

形式二:实现 Runnable 接口

编写 Runnable 接口实现类,实现 Runnable 接口的 run 办法。在创立 Thread 时将该 Runnable 类的实例作为一个参数来传递。

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

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

下列代码会创立并启动一个线程:

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

3. 线程的启动

3.1 启动零碎线程

执行 new java.lang.Thread().start() 会在操作系统上创立并启动一个原生线程。

  • java.lang.Thread#start 办法,外部调用了 native 的 start0 办法。该办法会由 JVM 映射到零碎层面创立并启动线程,将该线程与 java.lang.Thread 对象进行绑定,随后 JVM 会调用该 java.lang.Thread 对象的 run 办法。
  • 该操作的后果是会呈现两个并发执行的线程,一个是发动 Thread#start 调用的以后线程,另一个是新创建的会执行 Thread#run 的线程。
  • 留神,屡次启动一个线程是非法的。特地是当线程曾经完结执行后,不能再重新启动。

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 线程模型

依据《深刻了解 Java 虚拟机》第十二章第四节,实现线程次要有三种形式:

  • 应用内核线程实现(1:1 实现);
  • 应用用户线程实现(1:N 实现);
  • 应用用户线程加轻量级过程混合实现(N:M 实现)。

HotSpot 虚拟机采纳 1:1 的线程模型,它的 每一个 Java 线程都是间接映射到一个操作系统原生线程 来实现的,而且两头没有额定的间接构造,所以 HotSpot 本人是不会去干预线程调度的(能够设置线程优先级给操作系统提供调度倡议),全权交给底下的操作系统去解决,所以何时解冻或唤醒线程、该给线程调配多少处理器执行工夫、该把线程安顿给哪个处理器外围去执行等,都是由操作系统实现的,也都是由操作系统全权决定的。

  • 轻量级过程(Light Weight Process,LWP)是内核线程的一种高级接口,就是咱们通常意义上所讲的线程。程序个别不会间接应用内核线程,而是应用轻量级过程。
  • 内核线程(Kernel-Level Thread,KLT)就是间接由操作系统内核(Kernel)反对的线程,这种线程由内核来实现线程切换。
  • 内核(Kernel)通过操纵调度器(Scheduler)对线程进行调度,并负责将线程的工作映射到各个处理器上。

4. 线程的状态

4.1 状态定义

在 java.lang.Thread 类中,定义了 threadStatus 属性和 State 枚举来形容线程的状态。

/* 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 状态阐明

依据定义,线程具备 6 种状态:

  • 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.

线程在同一时间下,只会处于一种状态。这些状态是在 JVM 层面的,不会映射到操作系统层面的线程状态。

NEW

尚未启动的线程处于该状态。

RUNNABLE

在 JVM 中运行的线程处于该状态。
该状态下的线程正在 JVM 中运行,然而可能须要期待操作系统的资源,如 CPU 调度等。

BLOCKED

期待监视器锁(monitor lock)的线程处于这个状态。
分为两种状况:

  1. 首次进入 synchronized 块时期待获取锁;
  2. 在调用 Object#wait、Object.wait 办法之后,重入 synchronized 块时期待获取锁。

WAITING

无限期期待中的线程处于该状态。
调用以下办法之一,线程会处于该状态。

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

处于期待状态的线程,在期待的是其余线程执行特定的操作。

  • 调用 Object.wait 的线程,期待其余线程调用 Object.notify 或 Object.notifyAll
  • 调用 Thread.join 的线程,期待其余线程终止
  • 调用 LockSupport.park 的线程,期待其余线程调用 LockSupport.unpark

TIMED_WAITING

有限期期待中的线程处于该状态。
调用以下办法之一(入参均须要传入超时工夫),线程会处于该状态。

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

TERMINATED

已退出的线程处于该状态。
阐明线程曾经完结运行。

4.3 状态转移

4.4 状态分类

Java 线程 6 种状态看起来挺简单的,但其实 BLOCKED,WATTING,TIMED_WAITING 都会使线程处于阻塞状态,所以咱们将这三类都归类为阻塞状态。因而,Java 线程生命周期就能够简化为下图:

4.5 状态办法

在 java.lang.Thread 类中,定义了 getState() 办法获取线程的状态,定义了 isAlive() 办法判断以后线程是否沉闷。

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

利用 isAlive() 办法,能够用于判断以后 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#sleep、Object#wait、Thread#join 都会进入期待状态(WAITING/TIMED_WAITING)。

5.1 Thread#sleep

  • Thread#sleep 是一个动态的 native 办法。
  • 作用是使以后线程休眠一段时间,工夫单位为毫秒。并且休眠过程中不会开释任何监视器锁。
  • 调用了 Thread#sleep 之后,以后线程会进入 TIMED_WAITING 状态。
  • 如果其余线程中断了以后线程,则以后线程从休眠中被唤醒,会革除中断状态,并抛出 InterruptedException。
/**
 * 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;

Thread#sleep 还有另外一个版本,容许同时传入毫秒值和纳秒值,然而多进去的 nanos 最多只会让线程多休眠 1 毫秒,不罕用。Thread#join 和 Object#wait 中都具备相似的须要传入毫秒值和纳秒值的办法,后续不再赘述。

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

对于 Thread#sleep(0)

  • Thread.Sleep(0) 并非是真的要线程挂起 0 毫秒,意义在于这次调用 Thread.Sleep(0) 的以后线程的确的被解冻了一下,让其余线程有机会优先执行。
  • Thread.Sleep(0) 是以后线程临时放弃 CPU,也就是开释一些未用的工夫片给其余线程或过程应用,就相当于一个让位动作。

5.2 Object#wait

  • Object#wait 是一个非动态的 native 办法,须要在对象实例上应用。
  • 作用是使以后线程期待其余线程执行特定的操作,工夫单位为毫秒。
  • 调用 Object#wait 办法之前,线程必须持有监视器锁(monitor lock)。
  • 线程在期待过程中,会开释锁,进入 TIMED_WAITING 或 WAITING 状态。
  • 线程从期待中被唤醒,从新期待获取锁,进入 BLOCKED 状态。
/**
 * 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);
}

用法示例:

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

对于 Object#wait(0)

  • Object#wait() 外部实际上是调用 Object#wait(timeout),超时工夫为 0 毫秒,指的是没有超时工夫。
  • Object#wait(0) 被唤醒的条件:其余线程调用了 Object#notify 或 Object#notifyAll,或者以后线程产生了中断,或者产生虚伪唤醒(spurious wakeup)。
  • Object#wait(timeout) 被唤醒的条件:其余线程调用了 Object#notify 或 Object#notifyAll,或者以后线程产生了中断,或者产生虚伪唤醒,或者期待超时。

5.3 Thread#join

  • Thread#join 是一个非动态且非 native 办法,须要在线程实例上应用。
  • 作用是使以后线程期待指定线程执行结束,工夫单位为毫秒。
/**
 * 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;}
    }
}

调用 Thread#join 波及到两个线程之间的交互。

比方线程 threadA 在运行过程中,碰到 threadB.join() 这一行代码,示意 threadA 须要进入期待,直到 threadB 执行实现。

剖析一下整个过程:

  1. threadA 执行代码 threadB.join(),因为 Thread#join 办法是非动态的、由 synchronized 润饰的,threadA 首先须要获取 threadB 这个对象的锁。
  2. 若 threadA 获取不到锁,会进入阻塞状态 BLOCKED;若能够获取锁,则进入办法外部。
  3. 工夫参数校验通过后,则会执行 Thread#isAlive 校验线程是否存活。留神,这里的 this.isAlive() 是 threadB 对象的办法,因而是 threadA 来查看 threadB 是否存活。
  4. 若 threadB 还存活,则 threadA 执行 threadB.wait() 会开释锁,进入无限期期待状态 WAITING。
  5. 当 threadB 完结运行,进入 TERMINATED 状态,会调用 threadB.notifyAll() 办法唤醒在 threadB.wait() 上期待的线程。
  6. 当 threadA 从期待状态 WAITING 被唤醒后,从新获取 threadB 对象锁,循环查看 threadB 线程是否已存活,若不存活则完结期待。

JDK 源码正文中阐明了,当线程终止时,会调用 Object#notifyAll 办法。

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

示例代码:

@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();}

执行后果:

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

对于 Thread#join(0)

Thread#join() 外部实际上是调用 Thread#join(timeout),超时工夫为 0 毫秒,指的是没有超时工夫。

5.4 Thread#yield

线程调用 Thread#yield 并不会进入期待状态。

  • Thread#yield 是一个动态的 native 办法。
  • 作用是使以后线程让出 CPU。但对于 CPU 只是一个倡议,有可能一个线程刚让出 CPU,而后又立马取得了 CPU。
  • 与之绝对,Thread#sleep 办法肯定会让出 CPU 资源,并且休眠指定的工夫,不参加 CPU 的竞争。
  • 执行 Thread#yield 后线程仍处于 RUNNABLE 状态,而执行 Thread#sleep 后线程会从 RUNNABLE 变为 TIMED_WAITING 状态。
/**
 * 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)一个线程意味着在该线程实现工作之前进行其正在进行的所有,无效地停止其以后的操作。然而,没有任何语言方面的需要一个被中断的线程应该终止。中断一个线程只是为了引起该线程的留神,被中断线程能够决定如何应答中断。

此外,尽管 Thread.stop 的确进行了一个正在运行的线程,然而这种形式是不平安的,可能会产生不可意料的后果,在 JDK 中已标记为过期办法。

6.1 中断状态

在 Java 中,每一个线程都有一个中断标记位,该标记位用于示意线程的中断状态:已中断、未中断。

在 java.lang.Thread 中提供了查看、革除和设置中断标识的办法。

6.2 查看中断

/**
 * 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) 是一个 native 办法,用于查看线程是否已中断,入参 ClearInterrupted 用于管制是否革除中断状态。

调用该 native 办法的具备以下两个:

Thread#isInterrupted 查看指定线程的中断状态,不革除该线程的中断状态。

/**
 * 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 查看以后线程的中断状态,并革除以后线程的中断状态。

/**
 * 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 发动中断

  • 设置线程的中断状态为已中断。
  • 如果线程在调用 Object 类的 wait()、wait(long) 或 wait(long, int) 办法,或者调用 Thread 类的 join()、join(long)、join(long, int)、sleep(long) 或 sleep(long, int) 办法过程中碰壁,则其中断状态将被革除,它还将收到一个 InterruptedException。
  • 中断一个曾经终止的线程不会有任何影响。

也就是说,threadA 执行 threadB.interrupt(),则 threadB 会呈现以下两种后果之一(取决于 threadB 本身):

  • threadB 的中断状态为未中断,且抛出 InterruptedException;
  • threadB 的中断状态为已中断,不抛出异样。

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 如何正确处理中断

已知线程调用 Object#wait、Thread#join、Thread#sleep 办法进入期待状态时,会被其余线程调用 Thread#interrupt 唤醒。
此时以后线程的中断状态会被革除,并抛出 InterruptedException。
如果以后线程处于某种原因无奈传递 InterruptedException 异样,最好通过再次调用 interrupt 来复原中断的状态,以供下层调用者解决。

谬误的解决形式:

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

正确的解决形式:

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

或者

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

6.4 不要在循环查看中断

如果在循环中调用 sleep,除非正确地解决中断异样,否则不要去检测中断状态。

谬误的解决形式:

@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());
    }
}

该例子会呈现两种执行后果:

如果子线程在休眠当中被中断,因为会革除中断状态,导致子线程会有限循环上来。

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】休眠...

如果子线程在休眠过后被中断,因为会设置中断状态,子线程能够失去终止。

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

正确的解决形式:

@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 如何正确进行线程

  1. 工作中个别都会有循环构造,只有用一个标记管制住循环,就能够结束任务。
  2. 如果线程处于期待状态,无奈读取标记,此时能够应用 interrupt() 办法将线程从期待状态强制复原到运行状态中来,让线程具备 CPU 的执行资格,继而自行决定是否应该终止。

7. 总结

  1. 创立线程具备两种不同的含意,new Thread() 会在内存中创立 Thread 类实例,而 new Thread().start() 才会创立并启动操作系统原生线程。
  2. JVM 采纳了 1:1 的线程模型,每一个 Java 线程都是间接映射到一个操作系统原生线程。
  3. Thread 类中定义了 6 种线程状态,不会映射到操作系统层面的线程状态。
  4. Thread#sleep、Object#wait、Thread#join、Thread#yield 和 LockSupport 相干办法都能够用于调度线程。
  5. 线程中具备中断状态,该当应用中断来终止一个线程的运行。以后线程被中断时,须要正确地解决中断。

作者:Sumkor
链接:https://segmentfault.com/a/11…

正文完
 0