并发编程7 - 任务取消

简介: <p>通常如下情况会取消:</p> <p>1. 用户发起取消请求</p> <p>2. 现实的活动</p> <p>3. 分解任务其中一条发现了解决方案,其他的就可以取消了</p> <p>4. 分解任务其中一条发现了对于其他任务都有影响的错误,比如磁盘空间已满,其他的可以取消了</p> <p>5. 关闭,  当执行器关闭的时候,必须对正在处理及等待处理的任务进行优雅的关闭。</p>

通常如下情况会取消:

1. 用户发起取消请求

2. 现实的活动

3. 分解任务其中一条发现了解决方案,其他的就可以取消了

4. 分解任务其中一条发现了对于其他任务都有影响的错误,比如磁盘空间已满,其他的可以取消了

5. 关闭,  当执行器关闭的时候,必须对正在处理及等待处理的任务进行优雅的关闭。


一个最简单的方式是,加上取消标记,cancel方法设置取消标记。 主流程中判断取消标记,进行操作

public class TestCallable {
    public static void main(String[] args) {
        ThreadTest thread = new ThreadTest();
        thread.start();

        thread.cancel();
    }
}

class ThreadTest extends Thread{
    private boolean cancel = false;
    @Override
    public void run() {
        while(!cancel){
           doSomething();
        }
        System.out.println("被取消了");
    }

    private void doSomething() {
        // ...
    }

    public void cancel(){
        this.cancel = true;
    }
}

这样有个问题,如果doSomthing()的时候有阻塞,就永远不会监测到cancel的状态,  在JDK中我们还能使用专门为了取消而存在的中断方法。


上面的代码就会改为这样:

public class TestCallable {
    public static void main(String[] args) {
        ThreadTest thread = new ThreadTest();
        thread.start();

        thread.cancel();
    }
}

class ThreadTest extends Thread {
    @Override
    public void run() {
        try {
            while (!Thread.currentThread().isInterrupted()) {
                doSomething();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("被取消了");
    }

    private void doSomething() throws InterruptedException {
        // ...
        TimeUnit.SECONDS.sleep(1000);
    }

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

这里再来看一个用法,使用超时加上中断来决定任务执行多久:

public static void main(String[] args) {
        ScheduledExecutorService service = Executors.newScheduledThreadPool(1);
        final ThreadTest thread = new ThreadTest();
        service.schedule(new Runnable() {
            @Override
            public void run() {
                thread.cancel();
            }
        }, 1, TimeUnit.SECONDS);
        thread.start();
    }

中断策略

给线程加中断有一个原则就是一定要清楚中断策略,否则就不要使用中断方法。


使用Future完成取消

例如:

public class TestCallable {
    public static void main(String[] args) {
        ExecutorService service = Executors.newFixedThreadPool(1);
        ThreadTest thread = new ThreadTest();
        Future f = service.submit(thread);
        Future f2 = service.submit(new ThreadTest());
        try {
            f.get(1, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
            f.cancel(true);
            f2.cancel(false);
        }
    }
}

class ThreadTest extends Thread {
    @Override
    public void run() {
        System.out.println("开始运行1");

        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("执行完成1");
    }
}
其中断策略为:

.cancel(true),表示如果运行尝试对当前线程进行中断,一般是调用当前线程的.interrupt()方法

.cancel(false),表示如果没运行则不会运行这个线程了,如果运行了,会等到运行完成。

停止基于线程的服务

比如生产者消费者模式。

因为其使用了take()阻塞的方法,能够响应中断,所以如果生产者阻塞了不是问题,但是这样中断可能不太好。

我们可以加入状态标志,当设置了关闭标志之后,再生产就会抛出异常,  这个跟Executor的shutdown方法是一样的。

还有一种方式是使用致命药丸,就是在队列中加入一个特殊的任务,执行到这个药丸就停止服务。

使用TrackingExecutor类还能够获取已经取消了的任务, 用exec.getCancelledTasks()来获取


任务中的异常处理

在Executors.newFixedThreadPool(int , ThreadFactory threadFactory)中的第二个构造参数,是当一个线程异常中断的时候从这个factory中创建新的线程补充进去,可以用来做异常处理

或者一般情况下,使用Future.get方法可以得到异常


JVM关闭

正常关闭,System.exit()可以注册关闭钩子如下:

public class TestCallable {
    public static void main(String[] args) {
        Runtime.getRuntime().addShutdownHook(new Thread(){
            @Override
            public void run() {
                System.out.println("运行于JVM退出之前");
            }
        });
        System.exit(0);
    }
}


后台线程

daemon的线程,不会影响线程的退出。不会执行finally块,通常用于内部的事务处理。


避免使用Finalizer.不提供任何保证,并且会带来巨大的性能开销。

相关文章
|
CDN
静态资源库CDN服务
使用静态资源库可以访问线上资源文件,比如jquery库、bootstrap库。使用百度静态资源库的居多,但是发现百度暂时不支持https协议,bootcdn是一个不错的选择。
3634 0
|
机器学习/深度学习 传感器 边缘计算
基于深度学习的图像识别技术在自动驾驶中的应用####
随着人工智能技术的飞速发展,深度学习已成为推动自动驾驶技术突破的关键力量之一。本文深入探讨了深度学习算法,特别是卷积神经网络(CNN)在图像识别领域的创新应用,以及这些技术如何被集成到自动驾驶汽车的视觉系统中,实现对复杂道路环境的实时感知与理解,从而提升驾驶的安全性和效率。通过分析当前技术的最前沿进展、面临的挑战及未来趋势,本文旨在为读者提供一个全面而深入的视角,理解深度学习如何塑造自动驾驶的未来。 ####
553 1
|
存储 分布式数据库
hbase-region个数
hbase-region数量 单个regionserver配置region个数的两种方案: 根据官方推荐配置(硬盘容量);根据内存配置 一:官方推荐配置(硬盘容量): 官方文档给出的推荐: regionserver上的region个数范围在20~200;每个region的大小在10G~30G之间,比较符合实际。
3448 0
|
Kubernetes Linux 数据安全/隐私保护
使用kubeadm搭建高可用的K8s集群
使用kubeadm搭建高可用的K8s集群
|
小程序
云支付接入流程说明
云支付接入流程说明
7399 0
云支付接入流程说明
|
算法 C语言
数据结构学习笔记——顺序表的基本操作(超详细最终版+++)建议反复看看ヾ(≧▽≦*)o
数据结构学习笔记——顺序表的基本操作(超详细最终版+++)建议反复看看ヾ(≧▽≦*)o
数据结构学习笔记——顺序表的基本操作(超详细最终版+++)建议反复看看ヾ(≧▽≦*)o
|
JavaScript 前端开发 API
Webview的使用 | VSCode插件开发系列教程
其实VSCode也是基于electron框架的桌面软件,也就是说,你在VSCode里看到的所有的界面本就是网页。那在网页里再显示网页怎么做?相信你也想到了,就是iframe。
1764 0
Webview的使用 | VSCode插件开发系列教程
|
存储 编解码 安全
Studio One2023产品注册机最新版本下载
Studio One 6 中文特别版,现在Studio One 6终于有了视频支持,可以方便做视频配乐了。视频可以作为一个独立的音轨使用,跟乐器和音频音轨一样。你可以像音频素材一样在时间条来回拖拽视频来进行音画同步对齐。如果视频也包括了音频,那么你也可以导出音频作为一个子音轨来操作。视频音轨和子音轨也都有自己独立的混音通道可以进行各种处理,比如加载插件,设置路由等等。导出的格式支持Quicktime、MPEG-4、M4V。
2102 0
|
运维 Kubernetes 算法
独家深度 | 那些年我做开源和自研走过的弯路和经验
本文将重点分享开源结合自研项目的一些经验。
995 1
独家深度 | 那些年我做开源和自研走过的弯路和经验
|
机器学习/深度学习 城市大脑 人工智能
产品百科 |城市大脑是什么?
本文是对城市大脑的介绍,城市大脑的“三突破”是什么,城市大脑的整体架构以及城市大脑相关的解决方案的介绍。
产品百科 |城市大脑是什么?