停止当前线程有Thread.stop()方法,但是这个方法已经被标记为@Deprecated了,那么替代它的方法是什么呢?interrupt。
它有三个长得很像的方法:interrupt(),isInterrupted(),interrupted()。
第一:MyThreadA.interrupt():给MyThreadA线程设置一个中断标志。(注意,这里不同于stop()方法,并不是直接中断)。如果MyThreadA正在处于阻塞状态,那么MyThreadA线程将抛出一个 InterruptedException异常中断线程。
那么什么是阻塞状态呢?大佬的博客是这样说的:
如果线程被Object.wait, Thread.join和Thread.sleep三种方法之一阻塞,此时调用该线程的interrupt()方法,那么该线程将抛出一个 InterruptedException中断异常(该线程必须事先预备好处理此异常),从而提早地终结被阻塞状态。如果线程没有被阻塞,这时调用 interrupt()将不起作用,直到执行到wait(),sleep(),join()时,才马上会抛出 InterruptedException。
public class Test {
public static void main(String[] args) throws InterruptedException {
RunTest rt = new RunTest();
Thread t = new Thread(rt);
t.start();
t.interrupt();
}
}
class RunTest implements Runnable {
@Override
public void run() {
try {
for (long i = 0; i < 10000000L; i++) {
System.out.println("I'm doing a time consuming task");
}
// TimeUnit.SECONDS.sleep(2); 这条语句会被中断,抛出java.lang.InterruptedException异常
System.out.println("Thread finished.");
} catch (Exception e) {
e.printStackTrace();
System.out.println("Thread execption.");
}
}
}
RunTest线程是在循环一个非常大的数字,但它没有阻塞,所以interrupt()方法对它无效,RunTest线程会被正常执行完,打印Thread finished.
第二:MyThreadA.isInterrupted():判断MyThreadA线程是否设置了中断标记。需要注意的是,如果线程被中断且已经抛出了异常,它会清除中断标志。
public class Test {
public static void main(String[] args) throws InterruptedException {
RunTest rt = new RunTest();
Thread t = new Thread(rt);
t.start();
t.interrupt();
System.out.println(t.isInterrupted());
}
}
class RunTest implements Runnable {
@Override
public void run() {
try {
TimeUnit.SECONDS.sleep(2); //这条语句会被中断,抛出java.lang.InterruptedException
System.out.println("Thread finished.");
} catch (Exception e) {
System.out.println("Thread execption.");
//已经抛出了异常,会清除中断标记
System.out.println("isInterrupted? -->" + Thread.currentThread().isInterrupted());
}
}
}
第三:Thread.interrupted():这个是静态方法,直接Thread.interrupted()就好了,它会判断当前线程是否被设置了中断标志,并且清除中断标志。重点:静态方法,当前线程,清除中断标志。
其实,它和isInterrupted()是很像的,调用的是同一个方法。
public boolean isInterrupted() {
return isInterrupted(false);
}
/**
* 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);
}
isInterrupted(boolean flag):是否清除中断标记。
看一下interrupted()方法的注释:如果这个方法被成功调用两次,第二次会返回false。
if this method were to be called twice in succession, the
second call would return false.
public class Test {
public static void main(String[] args) throws InterruptedException {
RunTest rt = new RunTest();
Thread t = new Thread(rt);
t.start();
t.interrupt();
}
}
class RunTest implements Runnable {
@Override
public void run() {
try {
for (long i = 0; i < 100000L; i++) {
System.out.println("I'm doing a time consuming task");
}
System.out.println("Thread finished.");
System.out.println("1."+Thread.interrupted());
System.out.println("2."+Thread.interrupted());
} catch (Exception e) {
System.out.println("Thread execption.");
//已经抛出了异常,会清除中断标记
System.out.println("isInterrupted? -->" + Thread.currentThread().isInterrupted());
}
}
}
interrupt相关的三个方法就写完了,那么对于线程执行时间太长,但又不是阻塞的情况,该怎么办呢(例子中很大的for循环)?ThreadPoolExecutor.shutdown(),ThreadPoolExecutor.shutdownNow()以及ThreadPoolExecutor.awaitTermination(long timeout, TimeUnit unit),Future.get()方法都试过了,都不能解决。最后在shutdownNow()方法的注释找到了结果:
* <p>There are no guarantees beyond best-effort attempts to stop * processing actively executing tasks. For example, typical * implementations will cancel via {@link Thread#interrupt}, so any * task that fails to respond to interrupts may never terminate. *
用博主自己的意思润色翻译为:shutdownNow()方法只能尽可能的尝试去停止正在运行的线程任务,但并不保证一定能成功。比如,此方法的典型实现是通过给线程任务添加一个中断标记interrupt。但是呢,如果正在运行的线程任务对interrupt()方法不感冒的话(不属于Object.wait, Thread.join和Thread.sleep阻塞),那么这部分的线程任务是不会停止的。
其实以上列举的方法停止正在运行的线程,都是通过添加中断标记interrupt来实现的。所以对于上面的很大的for循环的例子,目前jdk还没有解决这种问题的方法。当然,Thread.stop()这类官方不推荐的方法我没有尝试,如果同学们有什么好的解决方案,欢迎留言一起探讨~