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 部分 问题引入

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

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

第2 部分 实例

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

 /**
* 7.21 在 ExecutorService 中跟踪在关闭之后取消的任务
* @ClassName: TrackingExecutor
* @author xingle
* @date 2014-11-12 下午8:39:33
*/
public class TrackingExecutor extends AbstractExecutorService{
private final ExecutorService exec;
private final Set<Runnable> tasksCancelledAtShutdown = Collections
.synchronizedSet(new HashSet<Runnable>()); public TrackingExecutor(ExecutorService exec){
this.exec = exec;
} public List<Runnable> getCancelledTasks(){
if(!exec.isTerminated())
throw new IllegalStateException();
return new ArrayList<Runnable>(tasksCancelledAtShutdown);
} /**
*
* @Description: TODO
* @param command
* @author xingle
* @data 2014-11-13 上午9:06:56
*/
@Override
public void execute(final Runnable runnable) {
exec.execute(new Runnable() { @Override
public void run() {
try{
runnable.run();
}finally{
if(isShutdown() && Thread.currentThread().isInterrupted())
tasksCancelledAtShutdown.add(runnable);
}
}
});
} /**
* 下面将ExecutorService 的其他方法委托给 exec
*/ /**
*
* @Description: TODO
* @author xingle
* @data 2014-11-13 上午9:06:56
*/
@Override
public void shutdown() {
exec.shutdown();
} /**
*
* @Description: TODO
* @return
* @author xingle
* @data 2014-11-13 上午9:06:56
*/
@Override
public List<Runnable> shutdownNow() {
return exec.shutdownNow();
} /**
*
* @Description: TODO
* @return
* @author xingle
* @data 2014-11-13 上午9:06:56
*/
@Override
public boolean isShutdown() {
return exec.isShutdown();
} /**
*
* @Description: TODO
* @return
* @author xingle
* @data 2014-11-13 上午9:06:56
*/
@Override
public boolean isTerminated() {
return exec.isTerminated();
} /**
*
* @Description: TODO
* @param timeout
* @param unit
* @return
* @throws InterruptedException
* @author xingle
* @data 2014-11-13 上午9:06:56
*/
@Override
public boolean awaitTermination(long timeout, TimeUnit unit)
throws InterruptedException {
return exec.awaitTermination(timeout, unit);
} }

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

 /**
* 7.22 使用TrackingExecutorService 来保存未完成的任务以备后续执行
* @ClassName: WebCrawler
* TODO
* @author xingle
* @date 2014-11-13 上午9:17:54
*/
public abstract class WebCrawler {
private volatile TrackingExecutor exec;
@GuardedBy("this")
public final Set<URL> urlsToCrawl = new HashSet<URL>(); private final ConcurrentMap<URL, Boolean> seen = new ConcurrentHashMap<URL, Boolean>();
private static final long TIMEOUT = 500;
private static final TimeUnit UNIT = TimeUnit.MICROSECONDS; public WebCrawler(URL startUrl){
urlsToCrawl.add(startUrl);
} public synchronized void start(){
exec = new TrackingExecutor(Executors.newCachedThreadPool());
for (URL url: urlsToCrawl)
submitCrawlTask(url);
urlsToCrawl.clear();
} /**
* 提交爬虫任务
* @param url
* @author xingle
* @data 2014-11-13 上午9:46:01
*/
private void submitCrawlTask(URL url) {
exec.execute(new CrawlTask(url));
} protected abstract List<URL> processPage(URL url); /**
* 保存未完成的
* @param urlsToCrawl
* @author xingle
* @data 2014-11-13 上午10:10:07
*/
private void saveUncrawled(List<Runnable> uncrawled) {
for (Runnable task:uncrawled){
URL url = ((CrawlTask)task).getPage();
System.out.println("保存未完成的URL:"+url);
urlsToCrawl.add(url);
} } //爬虫任务
private class CrawlTask implements Runnable{
private final URL url; CrawlTask(URL url){
this.url = url;
} private int count = 1; boolean alreadyCrawled() {
return seen.putIfAbsent(url, true) != null;
} void markUncrawled() {
seen.remove(url);
System.out.printf("marking %s uncrawled%n", url);
} @Override
public void run() {
for (URL link :processPage(url)){
if(Thread.currentThread().isInterrupted())
return;
System.out.println("提交的爬虫url:"+link);
submitCrawlTask(link);
}
} public URL getPage(){
return url;
}
} public synchronized void stop() throws InterruptedException{
try {
saveUncrawled(exec.shutdownNow());
if (exec.awaitTermination(100, UNIT)){
saveUncrawled(exec.getCancelledTasks());
} } finally {
exec = null;
}
}
}

测试程序:

 public class WebCrawler_Main {

     public static void main(String[] args) throws MalformedURLException{
WebCrawler webc = new WebCrawler(new URL("http://site.baidu.com/")) { @Override
protected List<URL> processPage(URL url) {
//获取该url下所有的链接
//这里省略了该功能
List<URL> url2 = new ArrayList<URL>();
try {
url2.add(new URL("http://www.cnblogs.com/xingele0917/"));
//url2.add(new URL("http://www.zhihu.com/"));
} catch (MalformedURLException e) {
e.printStackTrace();
}
return url2; } }; webc.start();
try {
Thread.sleep(10);
webc.stop();
} catch (InterruptedException e) {
e.printStackTrace();
}
} }

执行结果:

并发编程 12—— 任务取消与关闭 之 shutdownNow 的局限性的更多相关文章

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

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

  2. java并发编程-12个原子类

    背景 多线程更新变量的值,可能得不到预期的值,当然增加syncronized关键字可以解决线程并发的问题. 这里提供另外一种解决问题的方案,即位于 java.util.concurrent.atomi ...

  3. 多线程高并发编程(12) -- 阻塞算法实现ArrayBlockingQueue源码分析(1)

    一.前言 前文探究了非阻塞算法的实现ConcurrentLinkedQueue安全队列,也说明了阻塞算法实现的两种方式,使用一把锁(出队和入队同一把锁ArrayBlockingQueue)和两把锁(出 ...

  4. JUC 并发编程--12, 使用AtomicInteger 实现一把锁(排队自旋锁), 代码演示

    前面 使用自旋锁实现了一把锁,(请看 第5篇) volatile 三大特性: 可见性, 不保证原子性, 禁止指令重排 为了解决 volatile不保证原子性的问题, 引入了原子类, AtomicInt ...

  5. 并发编程 01—— ThreadLocal

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

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

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

  7. 并发编程 02—— ConcurrentHashMap

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

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

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

  9. 并发编程 05—— Callable和Future

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

随机推荐

  1. kafka 安装

    kafka是一个分布式的消息缓存系统 kafka集群中的服务器都叫做broker kafka有两类客户端,一类叫producer(消息生产者),一类叫做consumer(消息消费者),客户端和brok ...

  2. Linux的硬链接为何不能链接目录

    Linux中的目录文件是特殊的文件,其中的数据是一个关联列表的,像c++中的map,或者Python中的dict,保存每个文件名(包括子目录,Linux中一切皆文件!)到iNode的映射.iNode本 ...

  3. ubuntu 常用命令集合版(一)【大侠勿喷,菜鸟欢迎】(转载)

    1:apt-get:(一般是要加sudo) debian系系统的软件包管理程序(其图形化前端就是大名鼎鼎的新立得了),会自动帮你搞定依赖关系最常用参数:update        —-与你的软件源(在 ...

  4. 同事的游戏项目--Robocode-学习链接

    Robocode机器人库学习链接:http://www.pudn.com/search_db.asp?keyword=Robocode 官网 :http://robocode.sourceforge. ...

  5. C++之路进阶——bzoj3876(支线剧情)

    F.A.Qs Home Discuss ProblemSet Status Ranklist Contest ModifyUser  hyxzc Logout 捐赠本站 Notice:由于本OJ建立在 ...

  6. 0518 Scrum项目5.0

    一,组员任务完成情况 首页设计初步完成但是需要优化界面,只能简单的输出信息和在首页进行登录.界面极其简单. 鸡汤版面设计有困难,问题在于用何种形式来管理用户的数据上传,但是经过小组间的讨论确定设计方向 ...

  7. asp检测数字类型函数

    '**************************************************'函数ID:0014[检测ID是否为数字类型]'函数名:JCID'作 用:检测ID是否为数字类型' ...

  8. submit

    前台<body>中的代码: <body> <div id="top"> </div> <form id="login ...

  9. 通过FTP命令上传下载

      用命令如何把自己电脑上的文件上传到被入侵的电脑上呢.方法有很多.用ftp是个不错的选择.方法如下 echo open 你的ftpip >ftp.txt echo user >>f ...

  10. VIM 中鼠标选择不选中行号

    VIM 中鼠标选择不选中行号 在Vim中,我们一般会使用 :set nu 打开行号开关. 但是打开行号后,有个弊端,那就是在用鼠标进行选择的时候,会将前面的行号也一起进行拷贝了.但是在gVim中进行选 ...