Executor的常用方法

为了更好的控制多线程,JDK提供了一套线程框架Executor,帮助开发人员有效地进行线程控制。它们都在java.util.concurrent包中,是JDK开发包的核心。其中有一个重要的类:Executors,他扮演这线程工厂的角色,我们通过Executors可以创建特定功能的线程池。

  1. newFixedThreadPool()方法,该方法返回一个固定数量的线程池,该方法的线程数始终不变,当有一个任务提交时,如线程池中有空闲,则立即执行,如没有,则会被暂缓在一个任务队列中等待有空闲的线程去执行。
  2. newSingleThreadExecutor()方法,创建一个线程的线程池,若空闲则执行,若没有空闲线程则暂缓在任务队列中。
  3. newCachedThreadPool()方法,返回一个可以根据实际情况调整线程个数的线程池,不限制最大线程数量,若有空闲的线程则执行任务,若无任务则不创建线程。并且每一个空闲线程会在60秒后自动收回。
  4. newScheduleThreadPool()方法,返回一个ScheduledExecutorService对象,但该线程池可以指定线程的数量。

查看它们的源码:

    public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
} public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
} public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
} public static ScheduledExecutorService newScheduledThreadPool(
int corePoolSize, ThreadFactory threadFactory) {
return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
}
public ScheduledThreadPoolExecutor(int corePoolSize,
ThreadFactory threadFactory) {
//这里super是ThreadPoolExecutor类
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue(), threadFactory);
}

从源码看以看出它们都是由ThreadPoolExecutor构造出来的。

ThreadPoolExecutor构造方法概述

ThreadPoolExecutor(

  int corePoolSize,//核心线程数,线程池刚初始化的时候实例化线程个数

  int maximumPoolSize,//最大线程数

  long keepLongTime,//空闲时间,过时回收

  TimeUnit unit,//时间单位

  BlockingQueue<Runable> worker,//线程暂缓处

  ThreadFactory threadFactory,

  RejectExecuteHandle handle//拒绝执行的方法

)

所以上面几个方法主要是根据不同的参数来执行不同的行为。其中newScheduleThreadPool方法稍复杂一点,它的方法值是ScheduledExecutorService。

ScheduledExecutorService主要有两个方法scheduleWithFixedDelay,scheduleAtFixedRate

1、scheduleAtFixedRate 方法,以固定的频率来执行某项计划(任务),即固定的频率来执行某项计划,它不受计划执行时间的影响。到时间就执行。 它不受计划执行时间的影响。
2、scheduleWithFixedDealy,相对固定的延迟后,执行某项计划(任务), 即无论某个任务执行多长时间,等执行完了,我再延迟指定的时间去执行。它受计划执行时间的影响。

class Temp extends Thread {
public void run() {
try {
System.out.println( new Date().toString() + "..run start");
Thread.sleep(3000);
System.out.println( new Date().toString() + "...run end");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
} public class ScheduledJob { public static void main(String args[]) throws Exception { Temp command = new Temp();
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
System.out.println(new Date().toString() + "...start");
//scheduler.scheduleWithFixedDelay(command, 5, 2, TimeUnit.SECONDS);
scheduler.scheduleAtFixedRate(command, 5, 2, TimeUnit.SECONDS);
}
}

scheduleAtFixedRate 的执行结果:run方法延迟了5秒执行,run方法执行了3秒,已经超过了我们指定的间隔2秒,所以它在第一个run方法执行完后,立即执行了第二个run方法

Tue Feb 06 15:48:01 CST 2018...start
Tue Feb 06 15:48:06 CST 2018..run start
Tue Feb 06 15:48:09 CST 2018...run end
Tue Feb 06 15:48:09 CST 2018..run start
Tue Feb 06 15:48:12 CST 2018...run end
Tue Feb 06 15:48:12 CST 2018..run start

scheduleWithFixedDealy的执行结果:run方法延迟了5秒执行,run方法执行了3秒,已经超过了我们指定的间隔2秒,但是它还是等了2秒后,再执行了第二个run方法

Tue Feb 06 15:53:04 CST 2018...start
Tue Feb 06 15:53:10 CST 2018..run start
Tue Feb 06 15:53:13 CST 2018...run end
Tue Feb 06 15:53:15 CST 2018..run start
Tue Feb 06 15:53:18 CST 2018...run end
Tue Feb 06 15:53:20 CST 2018..run start
Tue Feb 06 15:53:23 CST 2018...run end

自定义线程池

如果Executors工厂类无法满足我们的需求,可以自己去创建线程池。在自己创建线程池时,这个构造方法对于队列是什么类型比较关键:

使用有届队列(ArrayBlockingQueue):若有新的任务需要执行时,如果线程池实际线程数小于corePoolSize,则优先创建线程,若大于corePoolSize,则会将任务加入队列中等待执行,若队列已满,则在总线程数不大于maximumPoolSize的前提下,创建行的线程,诺线程数大于maximumPoolSize,则执行拒绝策略。
使用无界队列(LinkedBlockingQueue):除非系统资源耗尽,否则无界的任务队列不存在入队失败的情况。当有新任务来时,系统的线程数小于corePoolSize时,则新建线程执行任务,当达到corePoolSize后,就不会继续增加,若后续还有新的任务加入,而没有空闲的线程资源,则任务直接进入队列等待。若任务创建和处理的速度差异很大时,无界队列会保持快速增长,直到耗尽系统内存。注意:maximumPoolSize在无界队列时没有作用
JDK拒绝策略:
AbortPolicy:直接抛出异常组织系统正常工作 CallerRunsPolicy:只要线程池未关闭,该策略直接在调用者线程中,运行当前被丢弃的任务
DiscardOldestPolicy:丢弃最老的一个请求,尝试再次提交当前任务
DiscardPolicy:丢弃无法处理的任务,不做任何处理。
如果需要自定义拒绝策略可以实现RejectedExecutionHandler接口

有届队列的例子

public class UseThreadPoolExecutor1 {
public static void main(String[] args) {
/**
* 在使用有界队列时,若有新的任务需要执行,如果线程池实际线程数小于corePoolSize,则优先创建线程,
* 若大于corePoolSize,则会将任务加入队列,
* 若队列已满,则在总线程数不大于maximumPoolSize的前提下,创建新的线程,
* 若线程数大于maximumPoolSize,则执行拒绝策略。或其他自定义方式。
*
*/
ThreadPoolExecutor pool = new ThreadPoolExecutor(
1, //coreSize
2, //MaxSize
60, //
TimeUnit.SECONDS,
new ArrayBlockingQueue<Runnable>(3) //指定一种队列 (有界队列)
//new LinkedBlockingQueue<Runnable>()
// , new MyRejected()
//, new DiscardOldestPolicy()
); MyTask mt1 = new MyTask(1, "任务1");
MyTask mt2 = new MyTask(2, "任务2");
MyTask mt3 = new MyTask(3, "任务3");
MyTask mt4 = new MyTask(4, "任务4");
MyTask mt5 = new MyTask(5, "任务5");
MyTask mt6 = new MyTask(6, "任务6"); pool.execute(mt1);
pool.execute(mt2);
pool.execute(mt3);
pool.execute(mt4);
pool.execute(mt5);
pool.execute(mt6); pool.shutdown(); }
}

只执行前四个任务(把任务5,6先不执行)

run taskId =1
run taskId =2
run taskId =3
run taskId =4

它是一个一个执行的,原因是第一个线程来的时候,立即执行了(coreSize=1),当后面三个线程来的时候会被加入到queue中(new ArrayBlockingQueue<Runnable>())

只执行前五个任务

run taskId =1
run taskId =5
run taskId =2
run taskId =3
run taskId =4

它是两个两个执行的,原因是第五个任务来的时候已经超出了queue的size(3),但是总线程数有小于MaxSize(2),所以任务5和任务1一起执行了,之后会从queue中拿两个再执行

执行留个任务

run taskId =1
Exception in thread "main" run taskId =5
java.util.concurrent.RejectedExecutionException: Task 6 rejected from java.util.concurrent.ThreadPoolExecutor@4e25154f[Running, pool size = 2, active threads = 2, queued tasks = 3, completed tasks = 0]
at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2047)
at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:823)
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1369)
at com.bjsxt.height.concurrent018.UseThreadPoolExecutor1.main(UseThreadPoolExecutor1.java:46)
run taskId =2
run taskId =3
run taskId =4

任务6来的时候,queue已经满了,总线程数也满了,这是会执行拒绝策略,JDK默认的是AbortPolicy,直接抛出异常,我们也可以改,就想代码里注释掉的一样

无界队列的例子

public class UseThreadPoolExecutor2 implements Runnable{

    private static AtomicInteger count = new AtomicInteger(0);

    @Override
public void run() {
try {
int temp = count.incrementAndGet();
System.out.println("任务" + temp);
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
} public static void main(String[] args) throws Exception{
//System.out.println(Runtime.getRuntime().availableProcessors());
BlockingQueue<Runnable> queue =
new LinkedBlockingQueue<Runnable>();
ExecutorService executor = new ThreadPoolExecutor(
5, //core
10, //max
120L, //2fenzhong
TimeUnit.SECONDS,
queue); for(int i = 0 ; i < 20; i++){
executor.execute(new UseThreadPoolExecutor2());
}
Thread.sleep(1000);
System.out.println("queue size:" + queue.size()); //
Thread.sleep(2000);
}
}

执行结果:

任务1
任务2
任务3
任务4
任务5
queue size:15
任务6
任务7
任务9
任务8
任务10
任务11
任务12
任务13
任务14
任务15
任务16
任务17
任务19
任务20
任务18

任务是5个5个执行的,而且把剩余的任务全部加到queue中。第二个参数没有作用,不会创建10个线程,如果是有届队列,就会起作用。

Task的代码:

public class MyTask implements Runnable {

    private int taskId;
private String taskName; public MyTask(int taskId, String taskName){
this.taskId = taskId;
this.taskName = taskName;
} public int getTaskId() {
return taskId;
} public void setTaskId(int taskId) {
this.taskId = taskId;
} public String getTaskName() {
return taskName;
} public void setTaskName(String taskName) {
this.taskName = taskName;
} @Override
public void run() {
try {
System.out.println("run taskId =" + this.taskId);
Thread.sleep(5*1000);
//System.out.println("end taskId =" + this.taskId);
} catch (InterruptedException e) {
e.printStackTrace();
}
} public String toString(){
return Integer.toString(this.taskId);
} }

MyRejected的代码:

public class MyRejected implements RejectedExecutionHandler{

    public MyRejected(){
} @Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
System.out.println("自定义处理..");
System.out.println("当前被拒绝任务为:" + r.toString());
} }

JDK多任务执行框架(Executor框架)的更多相关文章

  1. Java 进阶7 并行优化 JDK多任务执行框架技术

    Java 进阶7 并行优化 JDK多任务执行框架技术 20131114          Java 语言本身就是支持多线程机制的,他提供了 Thread 类 Runnable 接口等简单的多线程支持工 ...

  2. 那些年读过的书《Java并发编程实战》和《Java并发编程的艺术》三、任务执行框架—Executor框架小结

    <Java并发编程实战>和<Java并发编程的艺术>           Executor框架小结 1.在线程中如何执行任务 (1)任务执行目标: 在正常负载情况下,服务器应用 ...

  3. 《java并发编程实战》读书笔记5--任务执行, Executor框架

    第6章 任务执行 6.1 在线程中执行任务 第一步要找出清晰的任务边界.大多数服务器应用程序都提供了一种自然的任务边界选择方式:以独立的请求为边界. -6.6.1 串行地执行任务 最简单的任务调度策略 ...

  4. 详解Executor框架

    在Java中,使用线程来异步执行任务.Java线程的创建与销毁需要一定的开销,如果我们为每一个任务创建一个新线程来执行,这些线程的创建与销毁将消耗大量的计算资源.同时,为每一个任务创建一个新线程来执行 ...

  5. Java线程池 Executor框架概述

    线程池的意义 循环利用线程资源,避免重复创建和销毁线程 线程池的任务是异步执行的,只要提交完成就能快速返回,可以提高应用响应性 Java线程池还有一个很重要的意义:Java线程池就是JDK 5 推出的 ...

  6. 【Java多线程】Executor框架的详解

    在Java中,使用线程来异步执行任务.Java线程的创建与销毁需要一定的开销,如果我们为每一个任务创建一个新线程来执行,这些线程的创建与销毁将消耗大量的计算资源.同时,为每一个任务创建一个新线程来执行 ...

  7. 第十章 Executor框架

    在Java中,使用线程来异步执行任务.Java线程的创建与销毁需要一定的开销,如果我们为每一个任务创建一个新线程来执行,这些线程的创建与销毁将消耗大量的计算资源.同时,为每一个任务创建一个新线程来执行 ...

  8. Java并发框架:Executor

    介绍 随着当今处理器中可用的核心数量的增加, 随着对实现更高吞吐量的需求的不断增长,多线程 API 变得非常流行. Java 提供了自己的多线程框架,称为 Executor 框架. 1. Execut ...

  9. 《Java并发编程的艺术》第10章 Executor框架

    Java的线程既是工作单元,也是执行机制.从JDK5开始,把工作单元与执行机制分离开来.工作单元包括Runnable和Callable,执行机制由Executor框架提供. 10.1 Executor ...

随机推荐

  1. 前端精选文摘:css之GFC 神奇背后的原理(整理)

    CSS3 Grid Layout Web页面的布局,我们常见的主要有“浮动布局(float)”.“定位布局(position)”.“行内块布局(inline-block)”.“CSS3的多栏布局(Co ...

  2. Linux基础篇(1)

    1.计算机硬件的五大单元 五大单元:输入单元,输出单元,CPU内部的控制单元,算术逻辑单元与内存等五大部分. (Ps:CPU实际要处理的数据完全来自于内存) 2.计算机上常用的计算单位 (1)容量单位 ...

  3. OI数学知识清单

    OI常用的数学知识总结 本文持续更新…… 总结一下OI中的玄学知识 先列个单子,(from秦神 数论 模意义下的基本运算和欧拉定理 筛素数和判定素数欧几里得算法及其扩展[finish] 数论函数和莫比 ...

  4. 19.理解slop

    主要知识点: slop的含义(内在原理) slop的用法     一.slop的含义是什么?     query string(搜索文本)中的几个term,要经过几次移动才能与一个document匹配 ...

  5. Django——3 模板路径 模板变量 常用过滤器 静态文件的使用

    Django 模板路径 模板变量 过滤器 静态文件的加载 模板的路径,有两种方法来使用 设置一个总的templates在大项目外面,然后在sittings的TEMPLATES中声明 在每一个APP中创 ...

  6. scrapy——3 crawlSpider——爱问

    scrapy——3  crawlSpider crawlSpider 爬取一般网站常用的爬虫类.其定义了一些规则(rule)来提供跟进link的方便的机制. 也许该spider并不是完全适合您的特定网 ...

  7. 初识Scrapy——1—scrapy简单学习,伯乐在线实战、json数据保存

    Scrapy——1 目录 什么是Scrapy框架? Scrapy的安装 Scrapy的运行流程 Scrapy的使用 实战:伯乐在线案例(json文件保存) 什么是Scrapy框架? Scrapy是用纯 ...

  8. 球的序列(formation.*)

      N个编号为1-n的球,每个球都有唯一的编号.这些球被排成两种序列,分别为A.B序列,现在需要重新寻找一个球的序列l,对于这个子序列l中任意的两个球,要求j,k(j<k),都要求满足lj在A中 ...

  9. POJ 1190 生日蛋糕 剪枝

    Description 7月17日是Mr.W的生日,ACM-THU为此要制作一个体积为Nπ的M层生日蛋糕,每层都是一个圆柱体. 设从下往上数第i(1 <= i <= M)层蛋糕是半径为Ri ...

  10. 洛谷 P1481 魔族密码

    P1481 魔族密码 题目描述 风之子刚走进他的考场,就…… 花花:当当当当~~偶是魅力女皇——花花!!^^(华丽出场,礼炮,鲜花) 风之子:我呕……(杀死人的眼神)快说题目!否则……-_-### 花 ...