线程终止方法
Stop
这个方法就不介绍了,不推荐使用,已经被废弃了。会强制停止线程,进程和虚拟机会发生不可预料的状态。不优雅
/**
* Requests the receiver Thread to stop and throw ThreadDeath. The Thread is
* resumed if it was suspended and awakened if it was sleeping, so that it
* can proceed to throw ThreadDeath.
*
* @deprecated because stopping a thread in this manner is unsafe and can
* leave your application and the VM in an unpredictable state.
*/
@Deprecated
public final void stop() {
stop(new ThreadDeath());
}
/**
* Throws {@code UnsupportedOperationException}.
* @deprecated because stopping a thread in this manner is unsafe and can
* leave your application and the VM in an unpredictable state.
*/
@Deprecated
public final synchronized void stop(Throwable throwable) {
throw new UnsupportedOperationException();
}
/**
* Throws {@code UnsupportedOperationException}.
* @deprecated May cause deadlocks.
*/
@Deprecated
public final void suspend() {
throw new UnsupportedOperationException();
}
stopping a thread in this manner is unsafe and can leave your application and the VM in an unpredictable state.
Interrupts
原文地址: Java官方Interrupts介绍
An interrupt is an indication to a thread that it should stop what it is doing and do something else. It’s up to the programmer to decide exactly how a thread responds to an interrupt, but it is very common for the thread to terminate. This is the usage emphasized in this lesson.
程序员来决定是否中断进程
A thread sends an interrupt by invoking interrupt on the Thread object for the thread to be interrupted. For the interrupt mechanism to work correctly, the interrupted thread must support its own interruption.
Supporting Interruption
How does a thread support its own interruption? This depends on what it’s currently doing. If the thread is frequently invoking methods that throw InterruptedException, it simply returns from the run method after it catches that exception. For example, suppose the central message loop in the SleepMessages example were in the run method of a thread’s Runnable object. Then it might be modified as follows to support interrupts:
for (int i = 0; i < importantInfo.length; i++) {
// Pause for 4 seconds
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
// We've been interrupted: no more messages.
return;
}
// Print a message
System.out.println(importantInfo[i]);
}
Many methods that throw InterruptedException, such as sleep, are designed to cancel their current operation and return immediately when an interrupt is received.
当对象在阻塞状态时,Object.wait()/Thread.sleep()/Thread.join(),那么此时线程内部检查到中断flag为true,那么线程内部会抛出InterruptedException异常,然后可以在catch中捕获到该异常
同时由程序员决定是否,终止进程,可以直接return,run结束。如果不处理,那么是没法终止线程的
What if a thread goes a long time without invoking a method that throws InterruptedException? Then it must periodically invoke Thread.interrupted, which returns true if an interrupt has been received. For example:
for (int i = 0; i < inputs.length; i++) {
heavyCrunch(inputs[i]);
if (Thread.interrupted()) {
// We've been interrupted: no more crunching.
return;
}
}
In this simple example, the code simply tests for the interrupt and exits the thread if one has been received. In more complex applications, it might make more sense to throw an InterruptedException:
if (Thread.interrupted()) {
throw new InterruptedException();
}
This allows interrupt handling code to be centralized in a catch clause.
另一方面,如果线程没在阻塞状态,在运行状态,那么就不会检查中断flag,自然也不会去抛出捕获InterruptedException异常,所以程序不会终止。
解决方案可以通过Thread.interrupted()/isInterrupted方法(两个方法区别见如下)来手动检查中断flag,来决定是否终止程序
The Interrupt Status Flag
The interrupt mechanism is implemented using an internal flag known as the interrupt status. Invoking Thread.interrupt sets this flag. When a thread checks for an interrupt by invoking the static method Thread.interrupted, interrupt status is cleared. The non-static isInterrupted method, which is used by one thread to query the interrupt status of another, does not change the interrupt status flag.
By convention, any method that exits by throwing an InterruptedException clears interrupt status when it does so. However, it’s always possible that interrupt status will immediately be set again, by another thread invoking interrupt.
Thread.interrupt设置中断flag为true,静态方法Thread.interrupted会清空中断falg,非静态方法isInterrupted不会清空中断flag
任何一个抛出InterruptedException异常的方法都会清空中断flag
最后看看官方文档介绍:
/**
* Posts an interrupt request to this {@code Thread}. The behavior depends on
* the state of this {@code Thread}:
* <ul>
* <li>
* {@code Thread}s blocked in one of {@code Object}'s {@code wait()} methods
* or one of {@code Thread}'s {@code join()} or {@code sleep()} methods will
* be woken up, their interrupt status will be cleared, and they receive an
* {@link InterruptedException}.
* <li>
* {@code Thread}s blocked in an I/O operation of an
* {@link java.nio.channels.InterruptibleChannel} will have their interrupt
* status set and receive an
* {@link java.nio.channels.ClosedByInterruptException}. Also, the channel
* will be closed.
* <li>
* {@code Thread}s blocked in a {@link java.nio.channels.Selector} will have
* their interrupt status set and return immediately. They don't receive an
* exception in this case.
* <ul>
*
* @see Thread#interrupted
* @see Thread#isInterrupted
*/
public void interrupt()
- wait/join/sleep 状态时会清空中断flag,同时接收到InterruptedException异常
- 阻塞在I/O operation(java.nio.channels.InterruptibleChannel)接触的很少,设置自己的中断状态同时接收到ClosedByInterruptException,注意这个I/O是特殊的IO,不是我们常用的IO,我们的IO是java.io这个包,同时这个中断状态也是他们自己的,而不是上述的中断flag
- 阻塞在java.nio.channels.Selector,这玩意是啥,没接触过。设置自己的中断状态,直接return,不接收任何异常
代码示例
public class ThreadCon extends Thread
{
public void run()
{
for(int i = 0; i < Integer.MAX_VALUE; i++)
{
System.out.println(i);
}
}
public static void main(String[] args)
{
ThreadCon thread = new ThreadCon();
thread.start();
System.out.println("main thread");
try
{
TimeUnit.SECONDS.sleep(2);
}
catch(InterruptedException e)
{
e.printStackTrace();
}
thread.interrupt();
}
}
这是一段比较简单的示例,我们在开始的时候启动一个子线程,这个线程打印从0到Integer.MAX_VALUE 的值。而主线程先sleep 2秒钟。然后再尝试去中断子线程。如果我们去运行前面这一段代码,会发现子线程会一直在输出数字结果,它根本就不会停下来,说明,我们对一个子线程发interrupt的消息时,如果线程是在运行的状态之下,它会忽略这个请求而继续执行的。
最佳实践
源码
Thread.sleep方法
public static void sleep(long millis, int nanos) throws InterruptedException {
if (millis < 0) {
throw new IllegalArgumentException("millis < 0: " + millis);
}
if (nanos < 0) {
throw new IllegalArgumentException("nanos < 0: " + nanos);
}
if (nanos > 999999) {
throw new IllegalArgumentException("nanos > 999999: " + nanos);
}
// The JLS 3rd edition, section 17.9 says: "...sleep for zero
// time...need not have observable effects."
if (millis == 0 && nanos == 0) {
// ...but we still have to handle being interrupted.
if (Thread.interrupted()) {
throw new InterruptedException();
}
return;
}
long start = System.nanoTime();
long duration = (millis * NANOS_PER_MILLI) + nanos;
Object lock = currentThread().lock;
// Wait may return early, so loop until sleep duration passes.
synchronized (lock) {
while (true) {
sleep(lock, millis, nanos);
long now = System.nanoTime();
long elapsed = now - start;
if (elapsed >= duration) {
break;
}
duration -= elapsed;
start = now;
millis = duration / NANOS_PER_MILLI;
nanos = (int) (duration % NANOS_PER_MILLI);
}
}
}
if (millis == 0 && nanos == 0) 我猜测是从sleep中醒过来,然后执行Thread.interrupted()方法去检查中断flag并清空中断flag,如果为true,那么就抛出InterruptedException异常
调用Thread的interrupt会把中断flag置为true
Object.wait方法
public final native void wait() throws InterruptedException;
native表示调用了本地方法C/C++方法,参考sleep,可以猜测出内部其实肯定也调用了interrupted方法,检查中断flag,然后清空flag
优雅停止线程方案
MyThread提供一个quit接口供外部终止线程,本例中模拟线程一直在运行态,一直没被阻塞。也可以把Thread.sleep(1000);取消注释也是完全可以的,Thread.sleep(1000)如果调用了Thread.interrupt,那么会主动抛出InterruptedException异常而不需要手动去抛出啦
public class Test {
public static void main(String args[]) {
MyThread myThread = new MyThread();
myThread.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
myThread.quit();
//myThread.interrupt();
//myThread.stop();
System.out.println("main thread after 1s sleep");
}
};
class MyThread extends Thread {
private int count;
private boolean mQuit;
@Override
public void run() {
while (count < Integer.MAX_VALUE) {
try {
if (interrupted()) {
throw new InterruptedException();
}
count++;
System.out.println("count: " + count);
// Thread.sleep(1000);
} catch (InterruptedException e) {
if (mQuit) {
return;
}
continue;
}
}
}
public void quit() {
interrupt();
mQuit = true;
}
}
输出结果: 1s后,MyThread线程打印到113328就结束啦。。
……..
count: 113328
main thread after 1s sleep
注意这里必须调用myThread.quit();而不能调用myThread.interrupt();否则一直到打印到Integer.MAX_VALUE值
因为捕获到InterruptedException异常,如果mQuit为false,那么continue只会终止本次循环,而不会跳出本次循环
如果执行myThread.stop();
打印结果:
……
count: 105041
count: 105042
count: 105043count: 105043main thread after 1s sleep
count: 105043打印了两次,线程发生了不可预料的状态