在 Java 中,应用 Thread 类能够在操作系统层面创立线程,并绑定到对应的 Thread 类实例中。利用线程异步地执行工作,是并发编程的根底。本文通过浏览 Thread 源码,理解线程状态的定义,线程调度的相干办法,以及对线程中断的解决等。
本文基于 jdk1.8.0_91
1. 继承体系
线程类 Thread 实现了 Runnable 接口,并且具备一个 Runnable 属性,示意须要线程执行的工作。
Thread#run 办法外部调用了属性 Runnable 的 run 办法;若 Runnable 属性为空则什么也不做。
<code class="java">/** * 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 构造函数
<code class="java">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 办法:
<code class="java">/** * 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 ofThread
. This subclass should override therun
method of classThread
. 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 theRunnable
interface. That class then implements therun
method. An instance of the class can then be allocated, passed as an argument when creatingThread
, and started.
形式一:继承 Thread 类
编写 Thread 的子类,重写 Thread 类的 run 办法。创立该子类的实例以启动线程执行工作。
例如,计算大于某一规定值的质数的线程能够写成:
<code class="java">class PrimeThread extends Thread { long minPrime; PrimeThread(long minPrime) { this.minPrime = minPrime; } public void run() { // compute primes larger than minPrime ... } }
下列代码会创立并启动一个线程:
<code class="java">PrimeThread p = new PrimeThread(143); p.start();
形式二:实现 Runnable 接口
编写 Runnable 接口实现类,实现 Runnable 接口的 run 办法。在创立 Thread 时将该 Runnable 类的实例作为一个参数来传递。
<code class="java">class PrimeRun implements Runnable { long minPrime; PrimeRun(long minPrime) { this.minPrime = minPrime; } public void run() { // compute primes larger than minPrime ... } }
下列代码会创立并启动一个线程:
<code class="java">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
<code class="java">/** * 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 枚举来形容线程的状态。
<code class="java">/* 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)的线程处于这个状态。
分为两种状况:
- 首次进入 synchronized 块时期待获取锁;
- 在调用 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() 办法判断以后线程是否沉闷。
<code class="java">/** * 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 类实例是否绑定了零碎原生线程。
<code class="java">@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。
<code class="java">/** * 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 中都具备相似的须要传入毫秒值和纳秒值的办法,后续不再赘述。
<code class="java">/** * 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 状态。
<code class="java">/** * 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); }
用法示例:
<code class="java">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 办法,须要在线程实例上应用。
- 作用是使以后线程期待指定线程执行结束,工夫单位为毫秒。
<code class="java">/** * 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 执行实现。
剖析一下整个过程:
- threadA 执行代码
threadB.join()
,因为 Thread#join 办法是非动态的、由 synchronized 润饰的,threadA 首先须要获取 threadB 这个对象的锁。 - 若 threadA 获取不到锁,会进入阻塞状态 BLOCKED;若能够获取锁,则进入办法外部。
- 工夫参数校验通过后,则会执行 Thread#isAlive 校验线程是否存活。留神,这里的
this.isAlive()
是 threadB 对象的办法,因而是 threadA 来查看 threadB 是否存活。 - 若 threadB 还存活,则 threadA 执行
threadB.wait()
会开释锁,进入无限期期待状态 WAITING。 - 当 threadB 完结运行,进入 TERMINATED 状态,会调用
threadB.notifyAll()
办法唤醒在threadB.wait()
上期待的线程。 - 当 threadA 从期待状态 WAITING 被唤醒后,从新获取 threadB 对象锁,循环查看 threadB 线程是否已存活,若不存活则完结期待。
JDK 源码正文中阐明了,当线程终止时,会调用 Object#notifyAll 办法。
As a thread terminates the {@code this.notifyAll} method is invoked.
示例代码:
<code class="java">@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 状态。
<code class="java">/** * 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 查看中断
<code class="java">/** * 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 查看指定线程的中断状态,不革除该线程的中断状态。
<code class="java">/** * 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 查看以后线程的中断状态,并革除以后线程的中断状态。
<code class="java">/** * 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
<code class="java">/** * 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 来复原中断的状态,以供下层调用者解决。
谬误的解决形式:
<code class="java">void func() { try { Thread.sleep(50); } catch (InterruptedException e) { // nothing } }
正确的解决形式:
<code class="java">void func() throw InterruptedException { Thread.sleep(50); }
或者
<code class="java">void func() { try { Thread.sleep(50); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }
6.4 不要在循环查看中断
如果在循环中调用 sleep,除非正确地解决中断异样,否则不要去检测中断状态。
谬误的解决形式:
<code class="java">@Test public void dealInterrupt() { Thread subThread = new Thread(new Runnable() { @Override public void run() { int i = 0; while (!Thread.cu<b style="color:transparent">来源gao@dai!ma.com搞$代^码网</b>rrentThread().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】休眠...
正确的解决形式:
<code class="java">@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 如何正确进行线程
- 工作中个别都会有循环构造,只有用一个标记管制住循环,就能够结束任务。
- 如果线程处于期待状态,无奈读取标记,此时能够应用 interrupt() 办法将线程从期待状态强制复原到运行状态中来,让线程具备 CPU 的执行资格,继而自行决定是否应该终止。
7. 总结
- 创立线程具备两种不同的含意,
new Thread()
会在内存中创立 Thread 类实例,而new Thread().start()
才会创立并启动操作系统原生线程。 - JVM 采纳了 1:1 的线程模型,每一个 Java 线程都是间接映射到一个操作系统原生线程。
- Thread 类中定义了 6 种线程状态,不会映射到操作系统层面的线程状态。
- Thread#sleep、Object#wait、Thread#join、Thread#yield 和 LockSupport 相干办法都能够用于调度线程。
- 线程中具备中断状态,该当应用中断来终止一个线程的运行。以后线程被中断时,须要正确地解决中断。
作者:Sumkor
链接:https://segmentfault.com/a/11…