在没有分析线程池原理之前先来分析下为什么有任务拒绝的情况发生。

这里先假设一个前提:线程池有一个任务队列,用于缓存所有待处理的任务,正在处理的任务将从任务队列中移除。因此在任务队列长度有限的情况下就会出现新任务的拒绝处理问题,需要有一种策略来处理应该加入任务队列却因为队列已满无法加入的情况。另外在线程池关闭的时候也需要对任务加入队列操作进行额外的协调处理。

RejectedExecutionHandler提供了四种方式来处理任务拒绝策略

1、直接丢弃(DiscardPolicy)

2、丢弃队列中最老的任务(DiscardOldestPolicy)。

3、抛异常(AbortPolicy)

4、将任务分给调用线程来执行(CallerRunsPolicy)。

这四种策略是独立无关的,是对任务拒绝处理的四中表现形式。最简单的方式就是直接丢弃任务。但是却有两种方式,到底是该丢弃哪一个任务,比如可以丢弃当前将要加入队列的任务本身(DiscardPolicy)或者丢弃任务队列中最旧任务(DiscardOldestPolicy)。丢弃最旧任务也不是简单的丢弃最旧的任务,而是有一些额外的处理。除了丢弃任务还可以直接抛出一个异常(RejectedExecutionException),这是比较简单的方式。抛出异常的方式(AbortPolicy)尽管实现方式比较简单,但是由于抛出一个RuntimeException,因此会中断调用者的处理过程。除了抛出异常以外还可以不进入线程池执行,在这种方式(CallerRunsPolicy)中任务将有调用者线程去执行。

下面来看下这几种拒绝策略的例子。

使用直接丢弃任务本身的拒绝策略:DiscardPolicy

    import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit; public class ExecutorDemo { private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public static void main(String[] args) {
int corePoolSize = 1;
int maximumPoolSize = 1;
BlockingQueue queue = new ArrayBlockingQueue<Runnable>(1);
ThreadPoolExecutor pool = new ThreadPoolExecutor(corePoolSize, maximumPoolSize,
0, TimeUnit.SECONDS, queue ) ;
pool.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy ());
for(int i=0;i<10;i++){
final int index = i;
pool.submit(new Runnable(){ @Override
public void run() {
log(Thread.currentThread().getName()+"begin run task :"+index);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
log(Thread.currentThread().getName()+" finish run task :"+index);
} });
} log("main thread before sleep!!!");
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
log("before shutdown()"); pool.shutdown(); log("after shutdown(),pool.isTerminated=" + pool.isTerminated());
try {
pool.awaitTermination(1000L, TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
log("now,pool.isTerminated=" + pool.isTerminated());
} protected static void log(String string) {
System.out.println(sdf.format(new Date())+" "+string);
} }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59

运行结果:

    2016-08-04 22:29:21  main thread before sleep!!!
2016-08-04 22:29:21 pool-1-thread-1begin run task :0
2016-08-04 22:29:22 pool-1-thread-1 finish run task :0
2016-08-04 22:29:22 pool-1-thread-1begin run task :1
2016-08-04 22:29:23 pool-1-thread-1 finish run task :1
2016-08-04 22:29:25 before shutdown()
2016-08-04 22:29:25 after shutdown(),pool.isTerminated=false
2016-08-04 22:29:25 now,pool.isTerminated=true
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

从结果可以看出,只有task0和task1两个任务被执行了。

为什么只有task0和task1两个任务被执行了呢?

过程是这样的:由于我们的任务队列的容量为1.当task0正在执行的时候,task1被提交到了队列中但是还没有执行,受队列容量的限制,submit提交的task2~task9就都被直接抛弃了。因此就只有task0和task1被执行了。

使用丢弃任务队列中比较久的任务的拒绝策略:DiscardOldestPolicy

如果将拒绝策略改为:DiscardOldestPolicy(丢弃队列中比较久的任务)

运行结果为:

    2016-08-04 22:31:58  pool-1-thread-1begin run task :0
2016-08-04 22:31:58 main thread before sleep!!!
2016-08-04 22:31:59 pool-1-thread-1 finish run task :0
2016-08-04 22:31:59 pool-1-thread-1begin run task :9
2016-08-04 22:32:00 pool-1-thread-1 finish run task :9
2016-08-04 22:32:02 before shutdown()
2016-08-04 22:32:02 after shutdown(),pool.isTerminated=false
2016-08-04 22:32:02 now,pool.isTerminated=true
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

从结果可以看出,只有task0和task9被执行了。

使用将任务将由调用者线程去执行的拒绝策略:CallerRunsPolicy

如果将拒绝策略改为:CallerRunsPolicy(即不用线程池中的线程执行,而是交给调用方来执行)

运行结果为:

    2016-08-04 22:33:07  mainbegin run task :2
2016-08-04 22:33:07 pool-1-thread-1begin run task :0
2016-08-04 22:33:08 main finish run task :2
2016-08-04 22:33:08 mainbegin run task :3
2016-08-04 22:33:08 pool-1-thread-1 finish run task :0
2016-08-04 22:33:08 pool-1-thread-1begin run task :1
2016-08-04 22:33:09 pool-1-thread-1 finish run task :1
2016-08-04 22:33:09 main finish run task :3
2016-08-04 22:33:09 mainbegin run task :5
2016-08-04 22:33:09 pool-1-thread-1begin run task :4
2016-08-04 22:33:10 main finish run task :5
2016-08-04 22:33:10 mainbegin run task :7
2016-08-04 22:33:10 pool-1-thread-1 finish run task :4
2016-08-04 22:33:10 pool-1-thread-1begin run task :6
2016-08-04 22:33:11 main finish run task :7
2016-08-04 22:33:11 mainbegin run task :9
2016-08-04 22:33:11 pool-1-thread-1 finish run task :6
2016-08-04 22:33:11 pool-1-thread-1begin run task :8
2016-08-04 22:33:12 main finish run task :9
2016-08-04 22:33:12 main thread before sleep!!!
2016-08-04 22:33:12 pool-1-thread-1 finish run task :8
2016-08-04 22:33:16 before shutdown()
2016-08-04 22:33:16 after shutdown(),pool.isTerminated=false
2016-08-04 22:33:16 now,pool.isTerminated=true
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

从结果可以看出,没有任务被抛弃,而是将由的任务分配到main线程中执行了。

小结

关于线程池的任务拒绝策略,我们要理解并记住,有如下的四种:

1、直接丢弃(DiscardPolicy)

2、丢弃队列中最老的任务(DiscardOldestPolicy)。

3、抛异常

4、将任务分给调用线程来执行。

参考资料

1、http://www.blogjava.net/xylz/archive/2011/01/08/342609.html

《Java线程池》:任务拒绝策略的更多相关文章

  1. Java线程池的拒绝策略

    一.简介 jdk1.5 版本新增了JUC并发编程包,极大的简化了传统的多线程开发.前面文章中介绍了线程池的使用,链接地址:https://www.cnblogs.com/eric-fang/p/900 ...

  2. JDK线程池的拒绝策略

    关于新疆服务请求未带入来话原因的问题 经核查,该问题是由于立单接口内部没有成功调用接续的 “更新来电原因接口”导致的,接续测更新来电原因接口编码:NGCCT_UPDATESRFLAG_PUT ,立单接 ...

  3. SimpleThreadPool给线程池增加拒绝策略和停止方法

    给线程池增加拒绝策略和停止方法 package com.dwz.concurrency.chapter13; import java.util.ArrayList; import java.util. ...

  4. Dubbo里面线程池的拒绝策略

    Dubbo里面线程池的拒绝策略 public class AbortPolicyWithReport extends ThreadPoolExecutor.AbortPolicy { protecte ...

  5. dubbo线程池的拒绝策略

    jdk自带的原生的拒绝策略抛出的异常信息不够详细,而dubbo对拒绝策略进行了改写,抛出的信息更具有参考价值,值得我们借鉴. jdk自带的原生拒绝策略抛出的信息: // ThreadPoolExecu ...

  6. Java - "JUC线程池" 线程状态与拒绝策略源码分析

    Java多线程系列--“JUC线程池”04之 线程池原理(三) 本章介绍线程池的生命周期.在"Java多线程系列--“基础篇”01之 基本概念"中,我们介绍过,线程有5种状态:新建 ...

  7. Java 线程池 8 大拒绝策略,面试必问!

    前言 谈到java的线程池最熟悉的莫过于ExecutorService接口了,jdk1.5新增的java.util.concurrent包下的这个api,大大的简化了多线程代码的开发.而不论你用Fix ...

  8. java并发:线程池、饱和策略、定制、扩展

    一.序言 当我们需要使用线程的时候,我们可以新建一个线程,然后显式调用线程的start()方法,这样实现起来非常简便,但在某些场景下存在缺陷:如果需要同时执行多个任务(即并发的线程数量很多),频繁地创 ...

  9. 面试题-关于Java线程池一篇文章就够了

    在Java面试中,线程池相关知识,虽不能说是必问提,但出现的频次也是非常高的.同时又鉴于公众号"程序新视界"的读者后台留言让写一篇关于Java线程池的文章,于是就有本篇内容,本篇将 ...

  10. JUC(4)---java线程池原理及源码分析

    线程池,既然是个池子里面肯定就装很多线程. 如果并发的请求数量非常多,但每个线程执行的时间很短,这样就会频繁的创建和销毁 线程,如此一来会大大降低系统的效率.可能出现服务器在为每个请求创建新线程和销毁 ...

随机推荐

  1. [转载]How to Install Google Chrome 39 in CentOS/RHEL 6 and Fedora 19/18

    FROM: http://tecadmin.net/install-google-chrome-in-centos-rhel-and-fedora/ Google Chrome is a freewa ...

  2. Angular 学习笔记——service &constant

    <!DOCTYPE HTML> <html ng-app="myApp"> <head> <meta http-equiv="C ...

  3. 【IOS】mac终端运行.sh文件总是提示permission denied

    如果我目录jni有一个list.sh文件 我直接 nxgametekiMacBook-Air:jni luonan$  ./list.sh ../../Classes 提示 permission de ...

  4. VB断点调试

    最近都在敲机房收费系统,这个系统是我们第一次自己在没有源代码的情况下进行的系统. 写程序的时候逻辑非常重要,可是我们还要清楚非常多时候你以为的并非你以为的! 就像在敲机房的时候,我们明明理清了逻辑.并 ...

  5. mkdir的参数-p的作用

    mkdir -p /nfs 也就是加上-p参数,之前只知道是递归创建目录,于是就发问了,得到的答案是: -p, --parents              no error if existing, ...

  6. Android高级控件(三)——&#160;使用Google ZXing实现二维码的扫描和生成相关功能体系

    Android高级控件(三)-- 使用Google ZXing实现二维码的扫描和生成相关功能体系 摘要 如今的二维码可谓是烂大街了.到处都是二维码.什么都是二维码,扫一扫似乎已经流行到习以为常了,今天 ...

  7. EasyUI datagrid 过滤事件段

    //dateTimeRange过滤扩展 $.extend($.fn.datagrid.defaults.filters, { dateRange: { /*onInit: function(targe ...

  8. iOS项目工程添加.a文件遇到的Dsymutil Error

    将.a文件加入工程,很多教程讲的都是: 右键选择Add->Existing Files…,选择.a文件和相应的.h头文件.或者将这两个文件拖入XCode工程目录结构中,在弹出的界面中勾选Copy ...

  9. mysqldumpslow命令

    mysqldumpslow --help显示如下 Parse and summarize the MySQL slow query log. Options are --verbose verbose ...

  10. listItem选中状态高亮

    两种方法1.在adapter中添加方法changeSelected()int mSelect = 0; //mSelect为选中项public void changeSelected(int posi ...