(八)线程池与Executor 框架(太老半废弃掉)

一 用线程池好处

限制管理资源(执行任务)。 维护统计信息,如已完成任务数。 

1)重复利用已创建线程降低线程创建和销毁消耗。

2)提高响应速度。任务到达立即执行(不需等线程创建)。

3)统一分配,调优和监控

二 Executor 框架 

2.1 简介

Java 5之后, Executor启动线程Thread 的 start 好,除了更易管理、节约开销,避免 this 逃逸(构造函数返回之前,其他线程就持有该对象引用. 调用构造完全的对象方法引发错误)

2.2 Executor 框架结构(主要由三大部分组成)

1 任务。

Runnable接口(不返回结果)或Callable接口(返回)实现类都可以被ThreadPoolExecutorScheduledThreadPoolExecutor执行。

2 任务的执行

核心接口Executor ,ScheduledThreadPoolExecutorThreadPoolExecutor两个关键类实现了ExecutorService接口(继承Executor)

ScheduledThreadPoolExecutor继承了ThreadPoolExecutor并实现ScheduledExecutorService ,而ScheduledExecutorService又实现了ExecutorService

ThreadPoolExecutor类描述:

ScheduledThreadPoolExecutor类描述:

3 异步计算的结果

Future接口及实现类FutureTask类。 

Runnable接口Callable接口实现类提交(调用submit方法)给ThreadPoolExecutorScheduledThreadPoolExecutor时,会返回一个FutureTask对象

newTaskFor方法返回FutureTask对象。

2.3 Executor 框架的使用示意图


1.主线程首先要创建实现Runnable或者Callable接口的任务对象。 

备注: 工具类Executors可以实现Runnable对象和Callable对象之间的相互转换。

2.创建完成Runnable对象直接交给ExecutorService执行(ExecutorService.execute(Runnable command));或者也可以把Runnable对象或Callable对象提交给ExecutorService执行(ExecutorService.submit(Runnable task)或ExecutorService.submit(Callable task))。

执行execute()方法和submit()方法的区别是什么呢? 

1)execute()方法提交不需要返回值任务,无法判断任务是否被线程池执行成功与否; 

2)submit()方法用于提交需要返回值的任务。线程池会返回一个future类型的对象,判断任务执行成功,future的get()获取返回值,get()会阻塞当前线程直到任务完成get(long timeout,TimeUnit unit)阻塞一段时间后返回,有可能没执行完。

3.如果执行ExecutorService.submit(…),ExecutorService将返回一个实现Future接口的对象(我们刚刚也提到过了执行execute()方法和submit()方法的区别,到目前为止的JDK中,返回的是FutureTask对象)。由于FutureTask实现了Runnable,程序员也可以创建FutureTask,然后直接交给ExecutorService执行。

主线程FutureTask.get()等待任务执行完成。FutureTask.cancel(boolean mayInterruptIfRunning)取消此任务的执行。

三 ThreadPoolExecutor详解 

Executor 最核心的类

3.1 ThreadPoolExecutor类的四个比较重要的属性 

3.2 ThreadPoolExecutor类中提供的四个构造方法

最长的,其余三个都是在这个构造方法的基础上产生(给定某些默认参数的构造方法)线程池饱和策略

3.3 如何创建ThreadPoolExecutor

方式一:通过构造方法实现(建议第二种) 

方式二:通过Executor框架工具类Executors来实现,三种类型:

FixedThreadPool

SingleThreadExecutor

CachedThreadPool

对应Executors工具类中方法   

3.4 FixedThreadPool详解

重用固定线程线程池。

另外FixedThreadPool实现方法,和上面类似

FixedThreadPool的corePoolSizemaximumPoolSize都被设置为nThreads。 

上图说明:

当前运行的线程数小于corePoolSize,创建新线程执行任务;

                             等于corePoolSize,将任务加入LinkedBlockingQueue

执行完1中任务,循环从LinkedBlockingQueue获取执行

无界队列 LinkedBlockingQueue(容量为Intger.MAX_VALUE)影响: 

1. 达到corePoolSize后,新任务在无界队列中等待

2. maximumPoolSize、keepAliveTime无效参数; 

3. 运行中FixedThreadPool(未执行shutdown()或shutdownNow()方法)不拒绝任务

3.5 SingleThreadExecutor详解

单个worker线程Executor

corePoolSize和maximumPoolSize都为1.其他参数、LinkedBlockingQueue、执行图(流程)和FixedThreadPool相同。

3.6 CachedThreadPool详解

根据需要创建新线程的线程池

corePoolSize设置(0),maximumPoolSize被设置为Integer.MAX.VALUE,是无界主线程提交任务速度高maximumPool处理速度时,CachedThreadPool不断创建新线程。极端会耗尽cpu和内存资源。


上图说明: 

1. 先执行SynchronousQueue.offer(Runnable task)。如果当前maximumPool中有闲线程正在执行SynchronousQueue.poll(keepAliveTime,TimeUnit.NANOSECONDS),那么主线程执行offer操作与空闲线程执行的poll操作配对成功,主线程把任务交给空闲线程执行execute()完成,否则执行步骤2; 

2. 初始maximumPool为空,或maximumPool没有空闲线程时,将没线程执行SynchronousQueue.poll(keepAliveTime,TimeUnit.NANOSECONDS)。步骤1将失败,CachedThreadPool创建新线程execute完成;

3.7 ThreadPoolExecutor使用示例

3.7.1 示例代码

创建Runnable接口实现类(也可Callable接口)


编写测试程序,FixedThreadPool为例

3.7.2 shutdown()VS shutdownNow()

shutdown()关闭Executor上调用不再向DelayedPool添加任何其他任务(由ScheduledThreadPoolExecutor类在内部使用)。队列中提交任务允许完成。 

shutdownNow():终止当前正在运行的任务,并停止处理排队任务并返回正在等待执行的List

3.7.3 isTerminated() Vs isShutdown()

isShutDown:shutdown()或shutdownNow()返回为true

isTerminated: 关闭后所有任务都已完成,返回true。除非首先调用shutdown或shutdownNow,否则isTerminated永不为true。

四 ScheduledThreadPoolExecutor详解 

4.1 简介

定期执行任务任务队列DelayQueue封装了一个PriorityQueue排序队列任务,时间短执行(ScheduledFutureTask的time变量小),时间相同先提交先执行(ScheduledFutureTask的squenceNumber变量小)。

ScheduledThreadPoolExecutor和Timer的比较:

ScheduledThreadPoolExecutor:不敏感;任意数量线程(控制创建);捕获运行异常,需要时处理(重写afterExecute方法 ThreadPoolExecutor)。抛出异常任务取消,其他继续运行。

Timer:时钟变化敏感;一个线程;运行异常会杀死线程,死机;JDK1.5之后,不用Timer

4.2 ScheduledThreadPoolExecutor运行机制

ScheduledThreadPoolExecutor的执行主要分为两大部分:

1.当调用ScheduledThreadPoolExecutor的 scheduleAtFixedRate() 方法或者scheduleWirhFixedDelay() 方法时,会向ScheduledThreadPoolExecutor的 DelayQueue 添加一个实现了 RunnableScheduledFutur 接口的 ScheduledFutureTask 。

2.线程池中的线程从DelayQueue中获取ScheduledFutureTask,然后执行任务。

ScheduledThreadPoolExecutor为了实现周期性的执行任务,对ThreadPoolExecutor做了如下修改:

1使用 DelayQueue 作为任务队列;

2获取任务的方不同

3执行周期任务后,增加了额外的处理

1.线程1从DelayQueue中获取已到期的ScheduledFutureTask(DelayQueue.take())。到期任务是指ScheduledFutureTask的time大于等于当前系统的时间;

2.线程1执行这个ScheduledFutureTask;

3.线程1修改ScheduledFutureTask的time变量为下次将要被执行的时间;

4.线程1把这个修改time之后的ScheduledFutureTask放回DelayQueue中(DelayQueue.add())。

4.4 ScheduledThreadPoolExecutor使用示例

Runnable接口的类(我们上面的例子已经实现过)

ScheduledExecutorService和ScheduledThreadPoolExecutor实现java调度。


4.4.1 ScheduledExecutorService scheduleAtFixedRate(Runnable command,long initialDelay,long period,TimeUnit unit)方法

我们可以使用ScheduledExecutorService scheduleAtFixedRate方法来安排任务在初始延迟后运行,然后在给定的时间段内运行。

时间段是从池中第一个线程的开始,因此如果您将period指定为1秒并且线程运行5秒,那么只要第一个工作线程完成执行,下一个线程就会开始执行。


输出示例:

4.4.2 ScheduledExecutorService scheduleWithFixedDelay(Runnable command,long initialDelay,long delay,TimeUnit unit)方法

ScheduledExecutorService scheduleWithFixedDelay方法可用于以初始延迟启动周期性执行,然后以给定延迟执行。 延迟时间是线程完成执行的时间。




4.4.3 scheduleWithFixedDelay() vs scheduleAtFixedRate()

scheduleAtFixedRate(…)将延迟视为两个任务开始之间的差异(即定期调用) 

scheduleWithFixedDelay(…)将延迟视为一个任务结束与下一个任务开始之间的差异

scheduleAtFixedRate(): 创建并执行在给定的初始延迟之后,随后以给定的时间段首先启用的周期性动作; 那就是执行将在initialDelay之后开始,然后initialDelay+period ,然后是initialDelay + 2 * period ,等等。 如果任务的执行遇到异常,则后续的执行被抑制。 否则,任务将仅通过取消或终止执行人终止。 如果任务执行时间比其周期长,则后续执行可能会迟到,但不会同时执行。 

scheduleWithFixedDelay() : 创建并执行在给定的初始延迟之后首先启用的定期动作,随后在一个执行的终止和下一个执行的开始之间给定的延迟。 如果任务的执行遇到异常,则后续的执行被抑制。 否则,任务将仅通过取消或终止执行终止。

五 各种线程池的适用场景介绍

FixedThreadPool:限制当前线程数。适用负载比较重服务器;

SingleThreadExecutor:顺序执行单线程

CachedThreadPool:短期异步小程序,负载轻服务器;

ScheduledThreadPoolExecutor:后台周期任务,限制线程数

SingleScheduledThreadExecutor:单个后台周期任务,顺序执行

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,802评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,109评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,683评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,458评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,452评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,505评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,901评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,550评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,763评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,556评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,629评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,330评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,898评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,897评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,140评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,807评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,339评论 2 342

推荐阅读更多精彩内容