线程的中断

任务和线程的启动很容易。在大多数时候,我们都会让它们运行直到结束,或者让它们自行停止。然而,有时候我们希望提前结束任务或线程,或许是因为用户取消了操作,或者应用程序需要被快速闭要使任务和线程能安仝、快速、可靠地停止下来,并不是一件容易的事。Java的Thread类为我们提供了stop()suspend()等停止挂起线程的方法,但是由于安全问题目前都已被弃用。Java并没有提供一种安全的抢占式方法来停止线程,但它提供了中断(Interruption),这是一种协作机制,采用协作式的方式使一个线程终止另一个线程的当前工作。 这种协作式的方法是必要的,我们很少希望某个任务、线程或服务立即停止,因为这种立即停止会使共享的数据结构处于不一致的状态。相反,在编写任务和服务时可以使用一种协作的方式:当需要停止时,它们首先会清除当前正在执行的工作,然后再结束。这提供了更好的灵活性,因为任务本身的代码比发出取消请求的代码更清楚如何执行清除工作。 生命周期结束(End-of-Lifecycle)的问题会使任务、服务以及程序的设计和实现等过程变得复杂,而这个在程序设计中非常重要的要素却经常被忽略。一个在行为良好的软件与勉强运行的软件之间的最主要区别就是,行为良好的软件能很完善地处理失败、关闭和取消等过程。如何设计一种协作机制,让线程可以安全的中断呢?我们可以设置一个取消标志,在工作线程会被中断的地方去检查这个标志,当检查到这个中断标志被设置为已取消时,工作线程便开始做取消工作。

public class CancelableThread implements Runnable {
    
    // 线程取消标志,volatile修饰,保证内存可见性
    private volatile boolean isCanceled = false;
    
    @Override
    public void run() {
        while (!isCanceled) {//在工作线程中轮询检测这个取消标志
            System.out.println("The current thread is doing something...");
            System.out.println(Thread.currentThread().getName() + " cancel flag is " + isCanceled);
        }
        // 当取消标志被设置为true,执行以下代码,可以做一些取消工作
        System.out.println(Thread.currentThread().getName() + "The current thread Has been cancelled");
    }

    private void cancel() {
        isCanceled = true;
    }
}
public class MainTest {
    public static void main(String[] args) throws Exception {
        CancelableThread cancelableThread = new CancelableThread();
        new Thread(cancelableThread).start();
        try {
            Thread.sleep(100);
        } finally {
            // 设置标志位为true,来中断线程  
      cancelableThread.cancel();
        }
    }
}
  • 打印结果
Thread-0 cancel flag is false
The current thread is doing something...
Thread-0 cancel flag is false
The current thread is doing something...
Thread-0 cancel flag is false
The current thread is doing something...
Thread-0 cancel flag is false
The current thread is doing something...
Thread-0 cancel flag is true
Thread-0The current thread Has been cancelled 

总结一下上面的例子,这个例子有个缺陷,run方法如果这样写:

@Override
public void run() {
    while(!isCanceled){
        try {
            // 这里相当于线程被挂起了
            Thread.sleep(10000);
        }catch (InterruptedException e){
            // 这里用的是isInterrupted方法,标志位被清除了
        }
    }
    // 检测到中断标志,跳出循环
}

假如当前线程执行到了sleep方法,线程被挂起了,无论标志位isCanceled怎么变,根本跳不出循环,这样很可能导致和我们预期的结果不一致,所以不建议使用自定义的标志位来控制线程的中断,应当用下面的方法。

Thread类为我们提供了三个与线程中断相关的方法,来实现上述机制。这三个方法分别是:

public void interrupt() {
  //...  省略相关代码
  interrupt0();           // Just to set the interrupt flag       
  //...  省略相关代码
}
public static boolean interrupted() {
    return currentThread().isInterrupted(true);
}
public boolean isInterrupted() {
    return isInterrupted(false);
}
  • interrupt()方法主要用来设置中断标志位;如果此线程在调用wait方法或joinsleep方法阻塞时,那么它的中断状态将被清除(也就是线程不会理会中断标志位),并且会收到一个InterruptedException异常。
  • 静态的interrupted()方法用来测试当前线程是否被中断,调用此方法会清除线程的中断状态。如果当前线程被中断,则为true;否则为false。
  • isInterrupted()方法用来测试当前线程是否被中断,但是不会清除线程的中断状态。
public class InterruptTest {
    static class InnerThread extends Thread{
        @Override
        public void run() {
            while(!isInterrupted()){
                System.out.println(Thread.currentThread().getName()+" cancle flag is "+isInterrupted());
                try {
                    Thread.sleep(100);
                }catch (InterruptedException e){
                    e.printStackTrace();
                    // 抛出InterruptedException,中断标志位被清除,再次调用 interrupt();
                    interrupt();
                }
            }
            System.out.println(Thread.currentThread().getName()+" cancle flag is "+isInterrupted());
        }
    }


    public static void main(String[] args) {
        InnerThread innerThread = new InnerThread();
        innerThread.start();
        try {
            Thread.sleep(1000);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
        innerThread.interrupt();
//        InnerThread innerThread2 = new InnerThread();
//        innerThread2.start();
//        innerThread2.interrupt();
    }
}
  • 打印结果
Thread-0 cancle flag is false
Thread-0 cancle flag is false
Thread-0 cancle flag is false
Thread-0 cancle flag is false
Thread-0 cancle flag is false
Thread-0 cancle flag is false
Thread-0 cancle flag is false
Thread-0 cancle flag is false
Thread-0 cancle flag is false
Thread-0 cancle flag is false
java.lang.InterruptedException: sleep interrupted
    at java.lang.Thread.sleep(Native Method)
    at InterruptTest$InnerThread.run(InterruptTest.java:13)
Thread-0 cancle flag is true

文章出处(内容有所修改):https://www.cnblogs.com/perkins/p/9052139.html

总结一下:

  1. 一般情况下不建议使用自定义的中断标志位,原因上面说明了
  2. interruptisInterrupted方法配合使用,能解决绝大多数的中断业务
  3. 在调用waitsleepjoin等阻塞或者挂起方法的时候,会抛出中断异常,为什么这么设计?这是为了在线程由于某种原因被挂起后,后续的业务如果说还没执行完,强制中断线程会导致不可预料的后果(比如说文件写入了一半),如果就是想中断线程,可以在catch块中再次调用interrupt方法
  4. 静态的interrupted()isInterrupted()方法都是调用的private native boolean isInterrupted(boolean ClearInterrupted)根据传入的ClearInterrupted的值,来判断是否要清除中断标志位
  5. wait、notify、notifyAll为什么被定义在了Object类中?这些都跟锁有关,锁是属于对象的
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,905评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,140评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,791评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,483评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,476评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,516评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,905评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,560评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,778评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,557评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,635评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,338评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,925评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,898评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,142评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,818评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,347评论 2 342