概述
Java中关于线程停止提供了stop和interrupt两种方式,本文探究一下两者之间的不同。
分析
stop
/**
* Forces the thread to stop executing.
...
*/
@Deprecated(since="1.2")
public final void stop() {
SecurityManager security = System.getSecurityManager();
if (security != null) {
checkAccess();
if (this != Thread.currentThread()) {
security.checkPermission(SecurityConstants.STOP_THREAD_PERMISSION);
}
}
// A zero status value corresponds to "NEW", it can't change to
// not-NEW because we hold the lock.
if (threadStatus != 0) {
resume(); // Wake up thread if it was suspended; no-op otherwise
}
// The VM can handle all thread states
stop0(new ThreadDeath());
}
关于stop()的注释有很多,文中就不一一展示,大致如下:
- 该方式会强制停止线程。类似于电脑关机的时候没有点关机按钮,而是直接拔掉电源。
- 若是使用了security manager,或者还有其他线程,关掉的是其他线程,而不是当前线程,则可能会造成SecurityException。
- 若当前线程正在做其他异常操作,调用该方法会抛出ThreadDeath(一般用于重要的清除操作)异常,无法确定真正的异常。
- 该方法可以停止尚未启动的线程,若是线程又被启动了,则会马上停止。
- 该方法天然不安全,调用时会释放所有已上锁的monitors的锁,可能会导致无法意料的错误。
- @Deprecated(since=“1.2”)表明该方法已被废弃。
总而言之,该方法太粗暴,会导致好多好多错误,有些是莫名其妙的,有些可能会造成更大的错误,而且大多数情况下解决不了,所以能不用就尽量别用。
interrupt
相比于stop(),interrupt()较为温和。
public void interrupt() {
if (this != Thread.currentThread()) {
checkAccess();
// thread may be blocked in an I/O operation
synchronized (blockerLock) {
Interruptible b = blocker;
if (b != null) {
interrupted = true;
interrupt0(); // inform VM of interrupt
b.interrupt(this);
return;
}
}
}
interrupted = true;
// inform VM of interrupt
interrupt0();
}
该方法注释也有很多。
- 通常用于自己中断自己,也可能会抛出SecurityException异常。
- 若是线程被阻塞,比如调用了wait()、join()、sleep(long),中断状态会被清除,并且会收到中断异常InterruptedException。
- 若是阻塞了I/O操作,InterruptibleChannel会被关闭,则线程中断状态会被重新设置,也会收到ClosedByInterruptException。
- 若是线程没有存活,调用该方法也不会有任何副作用。
代码中最重要的变化是添加了blockerLock和interrupted,也就是调用该方法后,若该线程会被加上blockerLock锁,同时会被标记上中断中(interrupted = true),意味着线程任务若是没有执行完,会等到任务执行完后再关闭,只是标记该线程需要中断了,并不保证一定会中断,也可能完全不会中断,因为线程之间是协作式的,不是抢占式的。类似于正常关掉电脑,电脑会等所有任务都关闭后再关机,若是遇见更新或无法中断的任务,该电脑就一直无法关机。
interrupted与isInterrupted
与interrupt()相似的还有interrupted()和isInterrupted()。
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).
*
* @return {@code true} if the current thread has been interrupted;
* {@code false} otherwise.
* @see #isInterrupted()
* @revised 6.0, 14
*/
public static boolean interrupted() {
Thread t = currentThread();
boolean interrupted = t.interrupted;
// We may have been interrupted the moment after we read the field,
// so only clear the field if we saw that it was set and will return
// true; otherwise we could lose an interrupt.
if (interrupted) {
t.interrupted = false;
clearInterruptEvent();
}
return interrupted;
}
观察代码会发现:调用静态interrupted(),该方法会将正在中断的线程标志重新设为false,并清除中断事件。
测试:
public static void main(String[] args) {
ThreadPattern threadPattern = new ThreadPattern();
// 开启
threadPattern.start();
// 中断
threadPattern.interrupt();
}
/**
* Thread扩展类
*/
private static class ThreadPattern extends Thread{
@Override
public void run() {
super.run();
System.out.println(Thread.currentThread().getName() + "isInterrupted前:" + isInterrupted());
//
interrupted();
System.out.println(Thread.currentThread().getName() + "isInterrupted后:" + isInterrupted());
}
}
结果:
Thread-0isInterrupted前:true
Thread-0isInterrupted后:false
对比结果发现:调用threadPattern.interrupt()后isInterrupted状态为true,调用interrupted()后,isInterrupted状态变为false。确实改变了线程的中断状态。这样设计的目的可能是防止某些非常重要的任务不被中断吧。
isInterrupted
/**
* Tests whether this thread has been interrupted. The <i>interrupted
* status</i> of the thread is unaffected by this method.
*
* @return {@code true} if this thread has been interrupted;
* {@code false} otherwise.
* @see #interrupted()
* @revised 6.0, 14
*/
public boolean isInterrupted() {
return interrupted;
}
该方法就是返回线程的终止状态。
测试:
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName());
ThreadPattern threadPattern = new ThreadPattern();
// 开启
threadPattern.start();
// 中断
System.out.println(threadPattern.getName() + "isInterrupted前:" + threadPattern.isInterrupted());
threadPattern.interrupt();
System.out.println(threadPattern.getName() + "isInterrupted后:" + threadPattern.isInterrupted());
}
/**
* Thread扩展类
*/
private static class ThreadPattern extends Thread{
@Override
public void run() {
super.run();
System.out.println(Thread.currentThread().getName() + "isInterrupted:" + isInterrupted());
}
}
结果:
Thread-0isInterrupted前:false
Thread-0isInterrupted:false
Thread-0isInterrupted后:true
结果发现,isInterrupted()只是返回线程中断状态,没有其他任何操作。但线程中若是有抛出中断异常InterruptedException的话,线程的中断状态会被改为false;
测试:
private static class ThreadPattern extends Thread{
@Override
public void run() {
super.run();
System.out.println(getName() + ",isInterrupted前:" + isInterrupted());
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getName() + ",isInterrupted后:" + isInterrupted());
}
}
结果:
Thread-0,isInterrupted前:true
Thread-0,isInterrupted后:false
java.lang.InterruptedException: sleep interrupted
at java.base/java.lang.Thread.sleep(Native Method)
at thread.ThreadTest3$ThreadPattern.run(ThreadTest3.java:30)
线程sleep(1000)后,isInterrupted由原来的true变成了false,这样做可能是为了留出缓冲时间用于处理异常,开发中可以在异常处理中进行其他操作。若是还想继续中断,再次调用interrupt()即可。
测试:
/**
* Thread扩展类
*/
private static class ThreadPattern extends Thread{
@Override
public void run() {
super.run();
System.out.println(getName() + ",isInterrupted前:" + isInterrupted());
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println(getName() + ",isInterrupted后:" + isInterrupted());
// // 再次中断
interrupt();
}
System.out.println(getName() + ",isInterrupted后2:" + isInterrupted());
}
}
结果:
Thread-0,isInterrupted前:true
Thread-0,isInterrupted后:false
Thread-0,isInterrupted后2:true
java.lang.InterruptedException: sleep interrupted
at java.base/java.lang.Thread.sleep(Native Method)
at thread.ThreadTest3$ThreadPattern.run(ThreadTest3.java:30)
说明该线程确实被中断了。
总结
- stop()太粗暴,不考虑后果,不建议使用。
- interrupt()处理方式温和,建议使用。
- interrupted()会清除线程中断状态。
- isInterrupted()只返回线程中断状态。
- 线程出现InterruptedException(wait、sleep、interrupt、interrupted),线程中断状态会被清空,想要继续操作,需要再次调用interrupt()。