java取消线程实例

本文展示一个常见的取消线程的方法。

错误实例

class BrokenPrimeProducer extends Thread {
    private final BlockingQueue<BigInteger> queue;
    private volatile boolean cancelled = false;

    BrokenPrimeProducer(BlockingQueue<BigInteger> queue) {
        this.queue = queue;
    }

    public void run() {
        try {
            BigInteger p = BigInteger.ONE;
            while (!cancelled){
                queue.put(p = p.nextProbablePrime());
            }    
        } catch (InterruptedException consumed) {
        }
    }

    public void cancel() {
        cancelled = true;
    }
}

这里试图用一个标志来跳出while循环,理论上貌似可行,但是这里使用的是阻塞的操作,那么就出现一种场景,线程永远阻塞在put方法,根本就没来得及下个循环去判断cancelled这个条件,造成永远无法停止掉线程。

正确方法

通过中断来取消线程。

public class PrimeProducer extends Thread {
    private final BlockingQueue<BigInteger> queue;

    PrimeProducer(BlockingQueue<BigInteger> queue) {
        this.queue = queue;
    }

    public void run() {
        try {
            BigInteger p = BigInteger.ONE;
            while (!Thread.currentThread().isInterrupted()){
                queue.put(p = p.nextProbablePrime());
            }    
        } catch (InterruptedException consumed) {
            /* Allow thread to exit */
        }
    }

    public void cancel() {
        interrupt();
    }
}

这里的关键是queue的put操作能够响应interrupt方法,抛出InterruptedException,倒不是因为while条件里头的isInterrupted,这里while条件换成boolean可以照样可以。

小结

调用interrupt并不意味着立即停止目标线程正在进行的工作,而只是传递了请求中断的消息。对中断操作的正确理解是:它并不会真正地中断一个正在运行的线程,而只是发出中断请求,然后由线程在下一个合适的时刻中断自己。

有些方法,例如wait、sleep和join等,将严格地处理这种请求,当它们收到中断请求或者在开始执行时发现某个已被设置好的中断状态时,将抛出一个异常。设计良好的方法可以完全忽略这种请求,只要它们能使调用代码对中断请求进行某种处理。

设计糟糕的方法可能会屏蔽中断请求,从而导致调用栈中的其他代码无法对中断请求作出响应。在使用静态的interrupted时应该小心,因为它会清除当前线程的中断状态。如果在调用interrupted时返回了true,那么除非你想屏蔽这个中断,否则必须对它进行处理—可以抛出InterruptedException,或者通过再次调用interrupt来恢复中断状态。

原文发布于微信公众号 - 码匠的流水账(geek_luandun)

原文发表时间:2017-09-14

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏海说

类设计的SOLID原则

SOLID原则是面向对象范式的核心 单一职责原则(Single Responsible Principle, SRP):对于一个类,应该仅有一个引起它变化的原因...

2110
来自专栏编舟记

Java高编译低运行错误(ConcurrentHashMap.keySet)

本地使用maven编译和运行时一切都正常,但是通过ci的方式,编译、打包、发布到部署环境,运行时抛出了一条显而易见的JDK版本的错误。

1133
来自专栏蘑菇先生的技术笔记

那些年我们一起追过的缓存写法(三)

30610
来自专栏ChaMd5安全团队

360春秋杯3道web题的简单分析

360春秋杯3道web题的简单分析 From ChaMd5安全团队核心成员 pcat&香香 where is my cat 这题一开始很坑的,存在着/.git/...

4598
来自专栏Java架构

Java并发编程:AbstractQueuedSynchronizer的内部结构

  虽然已经有很多前辈已经分析过AbstractQueuedSynchronizer(简称AQS,也叫队列同步器)类,但是感觉那些点始终是别人的,看一遍甚至几遍...

811
来自专栏技术博客

设计模式之四(抽象工厂模式第一回合)

首先关于抽象工厂模式的学习,我们需要慢慢的,由浅入深的进入。不能单刀直入,否则可能达不到预期学明白的目标。

1121
来自专栏Java帮帮-微信公众号-技术文章全总结

Java并发学习3【面试+工作】

ReadWriteLock是jdk5中提供的读写分离锁。读写分离锁可以有效的帮助减少锁竞争,以提升性能。用锁分离的机制来提升性能非常容易理解,比如线程A1,A2...

1384
来自专栏JavaEdge

JUC源码分析之CyclicBarrier简介关键方法与参数源码解析CountDownLatch和CyclicBarrier的区别与联系应用场景小结

3498
来自专栏Golang语言社区

Golang工程经验(上)

作为一个C/C++的开发者而言,开启Golang语言开发之路是很容易的,从语法、语义上的理解到工程开发,都能够快速熟悉起来;相比C、C++,Golang语言更简...

5902
来自专栏YG小书屋

ES5.6 Bulk源码解析

4043

扫码关注云+社区

领取腾讯云代金券