热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

并发编程12——任务取消与关闭之shutdownNow的局限性

Java并发编程实践目录并发编程01——ThreadLocal并发编程02——ConcurrentHashMap并发编程03——阻塞队列和生产者-消费者模式并发编程04——闭锁Co

Java并发编程实践 目录

并发编程 01—— ThreadLocal

并发编程 02—— ConcurrentHashMap

并发编程 03—— 阻塞队列和生产者-消费者模式

并发编程 04—— 闭锁CountDownLatch 与 栅栏CyclicBarrier

并发编程 05—— Callable和Future

并发编程 06—— CompletionService : Executor 和 BlockingQueue

并发编程 07—— 任务取消

并发编程 08—— 任务取消 之 中断

并发编程 09—— 任务取消 之 停止基于线程的服务

并发编程 10—— 任务取消 之 关闭 ExecutorService

并发编程 11—— 任务取消 之 “毒丸”对象

并发编程 12—— 任务取消与关闭 之 shutdownNow 的局限性

并发编程 13—— 线程池的使用 之 配置ThreadPoolExecutor 和 饱和策略

并发编程 14—— 线程池 之 整体架构

并发编程 15—— 线程池 之 原理一

并发编程 16—— 线程池 之 原理二

并发编程 17—— Lock

并发编程 18—— 使用内置条件队列实现简单的有界缓存

并发编程 19—— 显式的Conditon 对象

并发编程 20—— AbstractQueuedSynchronizer 深入分析

并发编程 21—— 原子变量和非阻塞同步机制

概述
第1 部分 问题引入
第2 部分 实例
第1 部分 问题引入

  当通过 shutdownNow  来强行关闭 ExecutorService 时,它会尝试取消正在执行的任务,并返回所有已提交但尚未开始的任务,从而将这些任务写入日志或者保存起来以便之后进行处理。

  然而,我们无法通过常规方法来找出哪些任务已经开始但尚未结束。这意味着这我们无法在关闭过程中知道正在执行的任务的状态,除非任务本身会执行某种检查。要知道哪些任务还没有完成,你不仅需要知道哪些任务还没有开始,而且还需知道当 Executor 关闭时哪些任务正在执行。

第2 部分 实例

  在下面程序 TrackingExecutor 中给出了如何在关闭过程中判断正在执行的任务。通过封装 ExecutorService 并使得execute 记录哪些任务是在关闭后取消的,TrackingExecutor 可以找出哪些任务已经开始但还没有正常完成。在 Executor 结束后,getCancelledTasks 返回被取消的任务清单。

1 /**
2 * 7.21 在 ExecutorService 中跟踪在关闭之后取消的任务
3 * @ClassName: TrackingExecutor
4 * @author xingle
5 * @date 2014-11-12 下午8:39:33
6 */
7 public class TrackingExecutor extends AbstractExecutorService{
8 private final ExecutorService exec;
9 private final Set tasksCancelledAtShutdown = Collections
10 .synchronizedSet(new HashSet());
11
12 public TrackingExecutor(ExecutorService exec){
13 this.exec = exec;
14 }
15
16 public List getCancelledTasks(){
17 if(!exec.isTerminated())
18 throw new IllegalStateException();
19 return new ArrayList(tasksCancelledAtShutdown);
20 }
21
22 /**
23 *
24 * @Description: TODO
25 * @param command
26 * @author xingle
27 * @data 2014-11-13 上午9:06:56
28 */
29 @Override
30 public void execute(final Runnable runnable) {
31 exec.execute(new Runnable() {
32
33 @Override
34 public void run() {
35 try{
36 runnable.run();
37 }finally{
38 if(isShutdown() && Thread.currentThread().isInterrupted())
39 tasksCancelledAtShutdown.add(runnable);
40 }
41 }
42 });
43 }
44
45 /**
46 * 下面将ExecutorService 的其他方法委托给 exec
47 */
48
49 /**
50 *
51 * @Description: TODO
52 * @author xingle
53 * @data 2014-11-13 上午9:06:56
54 */
55 @Override
56 public void shutdown() {
57 exec.shutdown();
58 }
59
60 /**
61 *
62 * @Description: TODO
63 * @return
64 * @author xingle
65 * @data 2014-11-13 上午9:06:56
66 */
67 @Override
68 public List shutdownNow() {
69 return exec.shutdownNow();
70 }
71
72 /**
73 *
74 * @Description: TODO
75 * @return
76 * @author xingle
77 * @data 2014-11-13 上午9:06:56
78 */
79 @Override
80 public boolean isShutdown() {
81 return exec.isShutdown();
82 }
83
84 /**
85 *
86 * @Description: TODO
87 * @return
88 * @author xingle
89 * @data 2014-11-13 上午9:06:56
90 */
91 @Override
92 public boolean isTerminated() {
93 return exec.isTerminated();
94 }
95
96 /**
97 *
98 * @Description: TODO
99 * @param timeout
100 * @param unit
101 * @return
102 * @throws InterruptedException
103 * @author xingle
104 * @data 2014-11-13 上午9:06:56
105 */
106 @Override
107 public boolean awaitTermination(long timeout, TimeUnit unit)
108 throws InterruptedException {
109 return exec.awaitTermination(timeout, unit);
110 }
111
112
113 }

 

  在程序 WebCrawler 中给出了 TrackingExecutor 的用法。网页爬虫程序的工作通常是无穷尽的,因此当爬虫程序必须关闭时,我们通常希望保持它的状态,以便稍后重启动。CrawlTask 提供了一个 getPage 方法,该方法能找出正在处理的页面。当爬虫程序关闭时,无论是还没有开始的任务,还是那些被取消的任务,都将记录他们的URL,因此当爬虫程序程序启动时,就可以将这些URL 的页面抓取任务加入到任务队列中。

1 /**
2 * 7.22 使用TrackingExecutorService 来保存未完成的任务以备后续执行
3 * @ClassName: WebCrawler
4 * TODO
5 * @author xingle
6 * @date 2014-11-13 上午9:17:54
7 */
8 public abstract class WebCrawler {
9 private volatile TrackingExecutor exec;
10 @GuardedBy("this")
11 public final Set urlsToCrawl = new HashSet();
12
13 private final ConcurrentMap seen = new ConcurrentHashMap();
14 private static final long TIMEOUT = 500;
15 private static final TimeUnit UNIT = TimeUnit.MICROSECONDS;
16
17 public WebCrawler(URL startUrl){
18 urlsToCrawl.add(startUrl);
19 }
20
21 public synchronized void start(){
22 exec = new TrackingExecutor(Executors.newCachedThreadPool());
23 for (URL url: urlsToCrawl)
24 submitCrawlTask(url);
25 urlsToCrawl.clear();
26 }
27
28 /**
29 * 提交爬虫任务
30 * @param url
31 * @author xingle
32 * @data 2014-11-13 上午9:46:01
33 */
34 private void submitCrawlTask(URL url) {
35 exec.execute(new CrawlTask(url));
36 }
37
38 protected abstract List processPage(URL url);
39
40 /**
41 * 保存未完成的
42 * @param urlsToCrawl
43 * @author xingle
44 * @data 2014-11-13 上午10:10:07
45 */
46 private void saveUncrawled(List uncrawled) {
47 for (Runnable task:uncrawled){
48 URL url = ((CrawlTask)task).getPage();
49 System.out.println("保存未完成的URL:"+url);
50 urlsToCrawl.add(url);
51 }
52
53 }
54
55 //爬虫任务
56 private class CrawlTask implements Runnable{
57 private final URL url;
58
59 CrawlTask(URL url){
60 this.url = url;
61 }
62
63 private int count = 1;
64
65 boolean alreadyCrawled() {
66 return seen.putIfAbsent(url, true) != null;
67 }
68
69 void markUncrawled() {
70 seen.remove(url);
71 System.out.printf("marking %s uncrawled%n", url);
72 }
73
74 @Override
75 public void run() {
76 for (URL link :processPage(url)){
77 if(Thread.currentThread().isInterrupted())
78 return;
79 System.out.println("提交的爬虫url:"+link);
80 submitCrawlTask(link);
81 }
82 }
83
84 public URL getPage(){
85 return url;
86 }
87 }
88
89 public synchronized void stop() throws InterruptedException{
90 try {
91 saveUncrawled(exec.shutdownNow());
92 if (exec.awaitTermination(100, UNIT)){
93 saveUncrawled(exec.getCancelledTasks());
94 }
95
96 } finally {
97 exec = null;
98 }
99 }
100 }

 

测试程序:

1 public class WebCrawler_Main {
2
3 public static void main(String[] args) throws MalformedURLException{
4 WebCrawler webc = new WebCrawler(new URL("http://site.baidu.com/")) {
5
6 @Override
7 protected List processPage(URL url) {
8 //获取该url下所有的链接
9 //这里省略了该功能
10 List url2 = new ArrayList();
11 try {
12 url2.add(new URL("http://www.cnblogs.com/xingele0917/"));
13 //url2.add(new URL("http://www.zhihu.com/"));
14 } catch (MalformedURLException e) {
15 e.printStackTrace();
16 }
17 return url2;
18
19 }
20
21 };
22
23 webc.start();
24 try {
25 Thread.sleep(10);
26 webc.stop();
27 } catch (InterruptedException e) {
28 e.printStackTrace();
29 }
30 }
31
32 }

执行结果:

 

转:https://www.cnblogs.com/xingele0917/p/4094822.html



推荐阅读
  • 数据库内核开发入门 | 搭建研发环境的初步指南
    本课程将带你从零开始,逐步掌握数据库内核开发的基础知识和实践技能,重点介绍如何搭建OceanBase的开发环境。 ... [详细]
  • 本文详细介绍了Java编程语言中的核心概念和常见面试问题,包括集合类、数据结构、线程处理、Java虚拟机(JVM)、HTTP协议以及Git操作等方面的内容。通过深入分析每个主题,帮助读者更好地理解Java的关键特性和最佳实践。 ... [详细]
  • 本文深入探讨了Linux系统中网卡绑定(bonding)的七种工作模式。网卡绑定技术通过将多个物理网卡组合成一个逻辑网卡,实现网络冗余、带宽聚合和负载均衡,在生产环境中广泛应用。文章详细介绍了每种模式的特点、适用场景及配置方法。 ... [详细]
  • 本题探讨如何通过最大流算法解决农场排水系统的设计问题。题目要求计算从水源点到汇合点的最大水流速率,使用经典的EK(Edmonds-Karp)和Dinic算法进行求解。 ... [详细]
  • 深入解析 Apache Shiro 安全框架架构
    本文详细介绍了 Apache Shiro,一个强大且灵活的开源安全框架。Shiro 专注于简化身份验证、授权、会话管理和加密等复杂的安全操作,使开发者能够更轻松地保护应用程序。其核心目标是提供易于使用和理解的API,同时确保高度的安全性和灵活性。 ... [详细]
  • 本文探讨了在Java多线程环境下,如何确保具有相同key值的线程能够互斥执行并按顺序输出结果。通过优化代码结构和使用线程安全的数据结构,我们解决了线程同步问题,并实现了预期的并发行为。 ... [详细]
  • 本文详细介绍了 Apache Jena 库中的 Txn.executeWrite 方法,通过多个实际代码示例展示了其在不同场景下的应用,帮助开发者更好地理解和使用该方法。 ... [详细]
  • 从 .NET 转 Java 的自学之路:IO 流基础篇
    本文详细介绍了 Java 中的 IO 流,包括字节流和字符流的基本概念及其操作方式。探讨了如何处理不同类型的文件数据,并结合编码机制确保字符数据的正确读写。同时,文中还涵盖了装饰设计模式的应用,以及多种常见的 IO 操作实例。 ... [详细]
  • Hadoop入门与核心组件详解
    本文详细介绍了Hadoop的基础知识及其核心组件,包括HDFS、MapReduce和YARN。通过本文,读者可以全面了解Hadoop的生态系统及应用场景。 ... [详细]
  • libsodium 1.0.15 发布:引入重大不兼容更新
    最新发布的 libsodium 1.0.15 版本带来了若干不兼容的变更,其中包括默认密码散列算法的更改和其他重要调整。 ... [详细]
  • Scala 实现 UTF-8 编码属性文件读取与克隆
    本文介绍如何使用 Scala 以 UTF-8 编码方式读取属性文件,并实现属性文件的克隆功能。通过这种方式,可以确保配置文件在多线程环境下的一致性和高效性。 ... [详细]
  • 本文提供了使用Java实现Bellman-Ford算法解决POJ 3259问题的代码示例,详细解释了如何通过该算法检测负权环来判断时间旅行的可能性。 ... [详细]
  • 毕业设计:基于机器学习与深度学习的垃圾邮件(短信)分类算法实现
    本文详细介绍了如何使用机器学习和深度学习技术对垃圾邮件和短信进行分类。内容涵盖从数据集介绍、预处理、特征提取到模型训练与评估的完整流程,并提供了具体的代码示例和实验结果。 ... [详细]
  • 本次考试于2016年10月25日上午7:50至11:15举行,主要涉及数学专题,特别是斐波那契数列的性质及其在编程中的应用。本文将详细解析考试中的题目,并提供解题思路和代码实现。 ... [详细]
  • 在 Flutter 开发过程中,开发者经常会遇到 Widget 构造函数中的可选参数 Key。对于初学者来说,理解 Key 的作用和使用场景可能是一个挑战。本文将详细探讨 Key 的概念及其应用场景,并通过实例帮助你更好地掌握这一重要工具。 ... [详细]
author-avatar
mobiledu2502903717
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有