共计 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 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 办法。创立该子类的实例以启动线程执行工作。
例如,计算大于某一规定值的质数的线程能够写成:
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)的线程处于这个状态。
分为两种状况:
- 首次进入 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() 办法判断以后线程是否沉闷。
/**
* 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 执行实现。
剖析一下整个过程:
- 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.
示例代码:
@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 如何正确进行线程
- 工作中个别都会有循环构造,只有用一个标记管制住循环,就能够结束任务。
- 如果线程处于期待状态,无奈读取标记,此时能够应用 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…