首先解释一下为什么需要有线程中断。当我们操作一些软件时,会出现下面的情景。进入某个页面之后,会出现一个进度条,显示loading(意味着正在加载资源)。同时,我们也会发现在进度条的附近,会有一个"取消"按钮。当我们发现资源加载耗时很长,我们不想等下去的时候,单击“取消”按钮。这个时候就会退出资源加载的页面,返回上一个页面。这里的“取消”按钮操作就属于一种中断操作,它终止了资源加载这个操作的运行。
同理,在java中,我们也时常需要停止一些正在运行的线程。这个时候就需要使用到java线程的中断机制。java中的线程中断并不意味着线程停止运行,线程的中断只是线程的一个属性值。当你使用线程的中断机制中断线程后,这个值就会被修改。此时,调用相应的方法就可以检测到用户的中断请求。在检测到这个中断请求后,就会执行相应的中断逻辑代码;当然,在这中断逻辑代码中,是可以选择终止线程,也可以选择继续运行线程的,具体的逻辑需要使用者自己去定义。
在java中,只需要调用线程的interrupt()方法就可以发出线程中断请求。此时用户使用相应的检测方法就可以检测到这个请求。java中提供了两种线程中断检测方式:1、isInterrupt()方法;2、InterruptedException异常
一、isInterrupt()方法检测线程中断
public class ThreadTest09 {
public static void main(String[] args) throws Exception {
ThreadThread threadThread = new ThreadThread();
threadThread.start();
Thread.sleep(1000);
threadThread.interrupt();
}
}
public class ThreadThread extends Thread{
public ThreadThread(){}
@Override
public void run() {
while(true){
if(Thread.currentThread().isInterrupted()){
System.out.println("线程已中断,执行中断线程逻辑,停止线程运行!!");
return;
}
}
}
}
在上面的代码中,创建并启动了一个线程,线程执行一个死循环。在1秒钟后,调用interrupted方法发出中断请求;循环方法体中检测到线程中断请求时,则打印出信息,然后停止run方法发的执行。这就是一个简单的线程中断并停止线程运行的示例。当然,这里我们用return关键字直接结束了run方法的运行,如果我们在打印出了信息之后,不调用return 关键字,那么线程将会继续执行;也就是上面所说的线程中断了,并不代表线程会停止运行。下面的示例就演示了线程中断了,但还在继续执行:
public class ThreadTest09 {
public static void main(String[] args) throws Exception {
ThreadThread threadThread = new ThreadThread();
threadThread.start();
Thread.sleep(1000);
threadThread.interrupt();
}
}
public class ThreadThread extends Thread{
public ThreadThread(){
}
@Override
public void run() {
while(true){
if(Thread.currentThread().isInterrupted()){
System.out.println("线程已中断,执行中断线程逻辑,停止线程运行!!");
break; //这里不使用return 关键字,run方法会继续执行
}
}
System.out.println("线程继续运行!!");
}
}
接下来在对比一下Thread.interrupted()(也是用来检测线程是否中断的方法)方法和isInterrupted()方法,示例代码:
public class ThreadTest09 {
public static void main(String[] args) throws Exception {
ThreadThread threadThread = new ThreadThread();
threadThread.start();
Thread.sleep(1000);
threadThread.interrupt();
}
}
//使用isInterrupted方法
public class ThreadThread extends Thread{
public ThreadThread(){
}
@Override
public void run() {
while(true){
if(Thread.currentThread().isInterrupted()){
System.out.println("线程已中断,执行中断线程逻辑,线程继续运行!!");
}
if(Thread.currentThread().isInterrupted()){
System.out.println("再次检测,线程已中断,执行中断线程逻辑,停止线程运行!!");
return;
}
}
}
}
//输出结果:
//线程已中断,执行中断线程逻辑,线程继续运行!!
//再次检测,线程已中断,执行中断线程逻辑,停止线程运行!!
//使用Thread.Interrupted()方法
public class ThreadThread extends Thread{
public ThreadThread(){
}
@Override
public void run() {
while(true){
if(Thread.interrupted()){ //使用Thread.Interrupted()方法
System.out.println("线程已中断,执行中断线程逻辑,线程继续运行!!");
}
if(Thread.currentThread().isInterrupted()){
System.out.println("再次检测,线程已中断,执行中断线程逻辑,停止线程运行!!");
return;
}
}
}
}
//输出结果:
//线程已中断,执行中断线程逻辑,线程继续运行!!
对比上面的代码可以返现,都是检测线程是否中断的方法,但是执行结果却不一样。先看一下Thread.intterrupted()方法的源代码,再分析原因。源代码:
/**
* 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);
}
从源代码的注释中已经对刚才的情况进行了说明,大意是:检测一个线程是否是已经中断,如果线程的状态已经中断,则这个方法会返回true,同时清除线程的中断状态。也就是说调用这个静态的interrupted方法,除了返回线程是否中断之外,它还会清除线程的中断状态。再对比一下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);
}
源代码的注释部分有说明,大意是:检测一个线程是否已经中断,同时这个方法将不会影响线程的中断状态。换句话说就是isInterrupted方法不会修改线程的中断状态。
最后,线程如果调用isActive方法返回false时,就没有所谓的中断一说了。此时调用isInterrupted()方法返回的是false,并不意味着线程没有被中断。
二、InterruptedException异常
使用isInterrupted()方法存在一个不便利的地方,就是线程跨多个方法时,需要我们一层一层的去处理所有的中断逻辑,特别是递归调用的时候,这个缺点就更明显了。所以,在线程跨多个方法的时候,需要使用InterruptedException异常来响应中断请求。InterruptedException异常的使用是对isInterrupted()方法的一个补充。java文档对这个异常的解释如下:
/**
* Thrown when a thread is waiting, sleeping, or otherwise occupied,
* and the thread is interrupted, either before or during the activity.
* Occasionally a method may wish to test whether the current
* thread has been interrupted, and if so, to immediately throw
* this exception.
*/
这段话解释了
InterruptedException会被抛出的情况:
1、当线程处于waiting,sleeping或者其他占用状态时,将抛出这个异常
2、当线程在活动过程中或者激活之前,此时如果线程被中断,将抛出这个异常
3、当调用线程检测中断的方法检测到线程已经被中断的时候,用户可以手动抛出这个异常,作为对中断的响应。
上面对这个异常的解释已经很清晰,这里通过代码演示一下。代码如下:
public class ThreadTest10 {
public static void main(String[] args) throws Exception {
ThreadThread threadThread = new ThreadThread();
threadThread.start();
Thread.sleep(8000);//线程总共休眠6秒,8秒后中断线程,保证检测方法能够执行。
threadThread.interrupt();
}
}
//输出结果
//Fri Jan 12 12:12:07 CST 2018
//线程已中断,执行中断逻辑,停止运行线程!!
//Fri Jan 12 12:12:15 CST 2018 (相差8秒)
public class ThreadThread extends Thread{
public ThreadThread(){}
@Override
public void run() {
try {
getLock();
} catch (InterruptedException e) {
//捕获到中断异常,进行相应逻辑处理
System.out.println("线程已中断,执行中断逻辑,停止运行线程!!");
return;
}
}
private void getLock() throws InterruptedException {
TimeUnit.SECONDS.sleep(3);
getSubMethod();
}
private void getSubMethod() throws InterruptedException {
TimeUnit.SECONDS.sleep(3);
while(true){
if(Thread.interrupted()){ //检测线程是否中断
throw new InterruptedException();
}
}
}
}
如果将上面的main(主线程)的休眠时间改为2秒,此时启动的线程还处于getLock方法中;但是我们仍然可以接受线程终止的信息,而且是立即输出。这是因为sleep方法会对interrupted方法做出相应,抛出InterruptedException异常,也就是上面说的第一种情况。代码如下:
public class ThreadTest10 {
public static void main(String[] args) throws Exception {
ThreadThread threadThread = new ThreadThread();
threadThread.start();
Thread.sleep(2000);//线程总共休眠6秒,8秒后中断线程,保证检测方法能够执行。
threadThread.interrupt();
}
}
//输出结果
//Fri Jan 12 12:16:52 CST 2018
//线程已中断,执行中断逻辑,停止运行线程!!
//Fri Jan 12 12:16:54 CST 2018 (相差2秒)