Java并发编程实战系列7之取消与关闭

JAVA媒体提供任务机制来安全的终止线程。但是它提供了中断(interruption),这是一种写作机制,能够使一个线程终止另外一个线程。

一般来说没人希望立即终止,因为必要时总要先清理再终止。

开发一个应用能够妥善处理失败、关闭、取消等过程非常重要也有挑战。

7.1 任务取消

一定不要使用Thread.stop和suspend这些机制。

一种协作机制就是“标记位”。例如使用volatile类型的field来保存取消状态。

@ThreadSafe
public class PrimeGenerator implements Runnable {
    private static ExecutorService exec = Executors.newCachedThreadPool();

    @GuardedBy("this") private final List<BigInteger> primes
            = new ArrayList<BigInteger>();
    private volatile boolean cancelled;

    public void run() {
        BigInteger p = BigInteger.ONE;
        while (!cancelled) {
            p = p.nextProbablePrime();
            synchronized (this) {
                primes.add(p);
            }
        }
    }

    public void cancel() {
        cancelled = true;
    }

    public synchronized List<BigInteger> get() {
        return new ArrayList<BigInteger>(primes);
    }

    static List<BigInteger> aSecondOfPrimes() throws InterruptedException {
        PrimeGenerator generator = new PrimeGenerator();
        exec.execute(generator);
        try {
            SECONDS.sleep(1);
        } finally {
            generator.cancel();
        }
        return generator.get();
    }
}

1.1 中断

下面的例子会出现死锁,线程根本不会停止

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;
    }
}

interrupt 方法:中断目标线程。isInterrupted:返回目标线程的中断状态。静态的 interrupted方法:清除当前线程的中断状态,并返回它之前的值。大多数可中断的阻塞方法会在入口处检查中断状态

对中断操作(调用interrupt)的正确理解 他并不会真正的中断一个正在运行的线程,而只是发出中断请求,然后由线程在下一个合适的时候中断自己。比如,wait、sleep、 join等方法,当他们收到中断请求或开始执行时,发现某个已经被设置好的中断状态,则抛出异常interruptedException。

每个线程都有一个boolean类型的中断状态。当调用Thread.interrupt方法时,该值被设置为true,Thread.interruptted可以恢复中断。

阻塞库方法,例如sleep和wait、join都会检查中断,并且发现中断则提前返回,他们会清楚中断状态,并抛出InterruptedException。

但是对于其他方法interrupt仅仅是传递了中断的请求消息,并不会使线程中断,需要由线程在下一个合适的时刻中断自己。

通常,用中断是取消的最合理的实现方式。

上面的例子的改进方法就是

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();
    }
}

7.1.2 中断策略

发生了中断,需要尽快退出执行流程,并把中断信息传递给调用者,从而使调用栈中的上层代码可以采取进一步的操作。当然任务也可以不需要放弃所有操作,可以推迟处理中断清楚,知道某个时机。

7.1.3 响应中断

  • 传递异常
  • 回复中断状态
public class NoncancelableTask {
    public Task getNextTask(BlockingQueue<Task> queue) {
        boolean interrupted = false;
        try {
            while (true) {
                try {
                    return queue.take();
                } catch (InterruptedException e) {
                    interrupted = true;
                    // fall through and retry
                }
            }
        } finally {
            if (interrupted)
                Thread.currentThread().interrupt();
        }
    }

    interface Task {
    }
}

7.1.6 处理不可中断的阻塞

例如Socket I/O或者内置锁都不能响应中断,这时候该如何做才能终止他们呢?可以通过重写Thread.interrupt方法,例如加入close的逻辑。

7.2 停止基于线程的服务

7.2.1 示例:日志服务

7.2.2 关闭ExecutorService

7.2.3 Poison Pill

例如CloseEvent机制或者POISON对象,来做特殊的识别,从而让程序自己处理停止操作,退出线程。

7.3 处理非正常的线程终止

7.4 JVM关闭

第8章 线程池的使用

一个很好的ThreadPoolExecutor源码分析文档

ThreadPoolExecutor UML图:

image

image

image

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

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏owent

可执行文件压缩

最近看Rust相关东西的时候看到一篇关于压缩可执行文件的文章。压缩可执行文件对嵌入式开发特别有用,但是延伸一下用来减少我们游戏行业里预编译的工具二进制包大小和A...

4811
来自专栏java一日一条

Java线程池的分析和使用

合理利用线程池能够带来三个好处。第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。第二:提高响应速度。当任务到达 时,任务可以不需要的等...

751
来自专栏java思维导图

开源项目renren-fast解读,让java不再难懂(二)

1、百度百科的解释: XSS又叫CSS (Cross Site Script) ,跨站脚本攻击。它指的是恶意攻击者往Web页面里插入恶意html代码,当用户浏览...

2714
来自专栏丑胖侠

Zookeeper开源客户端Curator之事件监听详解

Curator对Zookeeper典型场景之事件监听进行封装,提供了使用参考。这篇博文笔者带领大家了解一下Curator的实现方式。 引入依赖 对于Curato...

5587
来自专栏zingpLiu

Linux内存信息查看——free命令

  free 命令可以显示系统已用和空闲的内存情况。包括物理内存、交互区内存(swap)和内核缓冲区内存(buffer)。共享内存将被忽略。在Linux系统监控...

1972
来自专栏测试开发架构之路

JMeter测试工具.jmx文件详解

摘要:了解.jmx文件格式类型,对jmeter二次开发与拓展有很大的帮助,当然也可以利用python对其进行一些处理(生成一些测试用例,对jmx文件进行 ”增删...

2994
来自专栏青玉伏案

TCP/IP协议族(二) HTTP报文头解析

本篇博客我们就来详细的聊一下HTTP协议的常用头部字段,当然我们将其分为请求头和响应头进行阐述。下方是报文头每个字段的格式,首先是头部字段的名称,如Accept...

2226
来自专栏智能算法

Python学习(九)---- python中的线程

原文地址: https://blog.csdn.net/fgf00/article/details/52773459 编辑:智能算法,欢迎关注! 上期我们一起学...

1642
来自专栏JavaEdge

Java线程池-ThreadPoolExecutor源码解析(基于Java8)

所以需要通过线程池协调多个线程,并实现类似主次线程隔离、定时执行、周期执行等任务.

3.3K9
来自专栏Create Sun

基础拾遗------webservice详解

前言   工作当中常用的服务接口有三个wcf,webservice和webapi.首先第一个接触的就是webservice,今天大致总结一下。 1.webser...

40611

扫码关注云+社区

领取腾讯云代金券