并发编程 — 详解线程池

本文将讲述如何通过JDK提供的API自定义定制的线程池

Java代码  

  1. //固定线程数 -- FixedThreadPool
  2. public static ExecutorService newFixedThreadPool(int nThreads) {
  3. return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS,
  4. new LinkedBlockingQueue<Runnable>());
  5. }
  6. //单条线程  --  SingleThreadExecutor
  7. public static ExecutorService newSingleThreadExecutor() {
  8. return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1, 0L,
  9. TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()));
  10. }
  11. //线程数自增长    --  CachedThreadPool
  12. public static ExecutorService newCachedThreadPool() {
  13. return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS,
  14. new SynchronousQueue<Runnable>());
  15. }

  从上面代码可以看出,通过Executors创建的三种线程池其实内部都是调用 
ThreadPoolExecutor进行创建

Java代码  

  1. public ThreadPoolExecutor(int corePoolSize,
  2. int maximumPoolSize,
  3. long keepAliveTime,
  4. TimeUnit unit,
  5. BlockingQueue<Runnable> workQueue) {
  6. this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
  7. Executors.defaultThreadFactory(), defaultHandler);
  8. }
corePoolSize 指定线程池中线程的数量
maximumPoolSize 指定线程池中最大线程数量
keepAliveTime 空闲线程存活时间,指超过corePoolSize的线程
unit keepAliveTime的时间单位
workQueue 任务队列,被提交但未被执行的任务存放容器
threadFactory 线程创建工厂,请参考上一章
handler 拒绝策略,当任务太多来不及处理时的拒绝策略

      需要重点注意的是workQueue和handler两个参数

workQueue: 
      参数workQueue是指被提交但未被执行的任务所存放的队列容器,它是一个BlockingQueue接口,仅用于存放Runaable对象.根据队列功能的分类,在ThreadPoolExecutor中可以使用两种队列

直接提交队列: 
      SynchronousQueue,SynchronousQueue是没有容量的容器,每一个插入的操作都需要等待相应的删除操作,SynchronousQueue不保存任务,它总是马上将任务提交给线程执行,如果没有空闲的线程则会尝试创建新的线程,如果线程数量已经达到最大值,则执行拒绝策略,使用SynchronousQueue通常需要设置很大的maximumPoolSize

有界的任务队列: 
      有界队列可以使用ArrayBlockingQueue,ArrayBlockingQueue的构造函数必须传入一个容量参数,表示队列的最大容量,当使用有界队列并有新任务时,若然线程池线程数量小于corePoolSize则会创建现场,若然大于corePoolSize则会将新任务加入任务队列,当任务队列已满无法加入时,则在总线程数不大于maximumPoolSize的前提下创建线程,若大于maximumPoolSize则执行拒绝策略,使用有界队列除非系统非常繁忙,否则确保核心线程数在corePoolSize

无界的任务队列: 
      无界任务队列可以使用LinkedBlockingQueue,与有界队列相比,除非系统资源耗尽,否则不会存在任务入队失败的情况.若任务创建和处理速度差异很大,无界队列会快速膨胀导致系统资源耗尽

优先任务队列: 
      优先任务队列使用PriorityBlockingQueue实现,PriorityBlockingQueue是一个特殊的无界队列,创建PriorityBlockingQueue时可以传入Comparator对任务进行优先级处理,PriorityBlockingQueue和无界队列可能会发生的问题一样,不过PriorityBlockingQueue能控制任务的优先级别

handler 
      handler参数指定了拒绝策略,即当任务数量超过线程池实际负载的时候,该如何处理被提交的任务,JDK内置提供了4种拒绝策略

AbortPolicy策略: 
      该策略会直接抛出异常

CallerRunsPolicy策略: 
      只要线程池未关闭,该策略直接在调用者线程中运行当前被放弃任务

DiscardOledestPolicy策略: 
      该策略丢弃最老的一个请求,即即将被执行的任务,并尝试再提交当前任务

DiscardPolicy策略: 
      该策略丢弃无法处理的任务,不做任何处理

所有拒绝策略都继承自RejectedExecutionHandler接口,读者可根据实际情况需要扩展该接口实现自己的拒绝策略

扩展线程池 
      ThreadPoolExecutor也是一个可扩展的线程池,它提供了beforeExecute,afterExecute,terminated3个接口对线程池调用任务进行控制

Java代码  

  1. public class TestThreadPoolExecutor extends ThreadPoolExecutor {
  2. @Override
  3. protected void beforeExecute(Thread t, Runnable r) {
  4. super.beforeExecute(t, r);
  5. }
  6. @Override
  7. protected void afterExecute(Runnable r, Throwable t) {
  8. super.afterExecute(r, t);
  9. }
  10. @Override
  11. protected void terminated() {
  12. super.terminated();
  13. }
  14. }

总结: 
      Executor框架提供了多功能的定制方式让开发者快速方便的实现定制的线程池,从而减少不必要的开发工作,且本身也提供多个扩展点让开发者自行扩展实现独特的业务逻辑

时间: 2024-10-13 00:46:55

并发编程 — 详解线程池的相关文章

Java并发编程:Java线程池核心ThreadPoolExecutor的使用和原理分析

目录 引出线程池 Executor框架 ThreadPoolExecutor详解 构造函数 重要的变量 线程池执行流程 任务队列workQueue 任务拒绝策略 线程池的关闭 ThreadPoolExecutor创建线程池实例 参考: 引出线程池 线程是并发编程的基础,前面的文章里,我们的实例基本都是基于线程开发作为实例,并且都是使用的时候就创建一个线程.这种方式比较简单,但是存在一个问题,那就是线程的数量问题. 假设有一个系统比较复杂,需要的线程数很多,如果都是采用这种方式来创建线程的话,那么

JAVA - 并发编程 - 执行器和线程池

思考? 1 为什么要使用执行器和线程池? 2 执行器和线程是什么?怎么使用 执行器 线程执行器分离了任务的创建和执行,提高了线程的性能 线程池 避免了频繁地创建和销毁线程,达到线程对象的重用,可以根据项目灵活地控制并发的数量 ExecutorService (java.util.concurrent) 1 Executors.newCachedThreadPool() 可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程 2 Executors.newFixedT

并发编程—7.2线程池基础

目录 1.什么是线程池?为什么要用线程池? 2.实现一个我们自己的线程池 3.JDK中的线程池和工作机制 3.1 线程池的创建 3.2 提交任务 3.3 关闭线程池 3.4 工作机制 4.合理配置线程池 5.预定义的线程池 6.Executor框架 7.了解CompletionService 1.什么是线程池?为什么要用线程池? 降低资源的消耗.线程的创建和销毁 提高响应速度.线程的创建时间为T1,执行时间T2,销毁时间T3,免去T1和T3的时间 提高线程的可管理性. 2.实现一个我们自己的线程

Java多线程编程详解

线程的同步 由于同一进程的多个线程共享同一片存储空间,在带来方便的同时,也带来了访问冲突这个严重的问题.Java语言提供了专门机制以解决这种冲突,有效避免了同一个数据对象被多个线程同时访问. 由于我们可以通过 private 关键字来保证数据对象只能被方法访问,所以我们只需针对方法提出一套机制,这套机制就是 synchronized 关键字,它包括两种用法:synchronized 方法和 synchronized 块. 1. synchronized 方法:通过在方法声明中加入 synch

Qt Quick 之 QML 与 C++ 混合编程详解

Qt Quick 技术的引入,使得你能够快速构建 UI ,具有动画.各种绚丽效果的 UI 都不在话下.但它不是万能的,也有很多局限性,原来 Qt 的一些技术,比如低阶的网络编程如 QTcpSocket ,多线程,又如 XML 文档处理类库 QXmlStreamReader / QXmlStreamWriter 等等,在 QML 中要么不可用,要么用起来不方便,所以呢,很多时候我们是会基于这样的原则来混合使用 QML 和 C++: QML 构建界面, C++ 实现非界面的业务逻辑和复杂运算. 请给

MFC下CSocket编程详解

MFC下CSocket编程详解 分类: C/C++2008-03-13 09:01 34465人阅读 评论(34) 收藏 举报 mfc编程socket服务器socketsstream MFC下CSocket编程详解: 1. 常用的函数和注意事项(详细的函数接口说明请查看MSDN): CSocket::Create 初始化(一般写服务器程序都不要用为好,用下面的 CSocket::Socket 初始化) CSocket::Socket初始化 CSocket::SetSockOpt 设置socket

[顶]ORACLE PL/SQL编程详解之二:PL/SQL块结构和组成元素(为山九仞,岂一日之功)

原文:[顶]ORACLE PL/SQL编程详解之二:PL/SQL块结构和组成元素(为山九仞,岂一日之功) [顶]ORACLE PL/SQL编程详解之二: PL/SQL块结构和组成元素(为山九仞,岂一日之功) 继上四篇:ORACLE PL/SQL编程之八:把触发器说透                ORACLE PL/SQL编程之六:把过程与函数说透(穷追猛打,把根儿都拔起!)                [推荐]ORACLE PL/SQL编程之四:把游标说透(不怕做不到,只怕想不到) [推荐]

【强烈强烈推荐】《ORACLE PL/SQL编程详解》全原创(共八篇)--系列文章导航

原文:[强烈强烈推荐]<ORACLE PL/SQL编程详解>全原创(共八篇)--系列文章导航 <ORACLE PL/SQL编程详解> 系列文章目录导航 ——通过知识共享树立个人品牌. 本是成书的,但后来做其他事了,就无偿的贡献出来,被读者夸其目前为止最“实在.经典”的写ORACLE PL/SQL编程的文章-! 觉得对你有帮助,请留言与猛点推荐,谢谢. [推荐]ORACLE PL/SQL编程详解之一:PL/SQL 程序设计简介(千里之行,始于足下) 本篇主要内容如下:第一章 PL/S

[推荐]ORACLE PL/SQL编程详解之三:PL/SQL流程控制语句(不给规则,不成方圆)

原文:[推荐]ORACLE PL/SQL编程详解之三:PL/SQL流程控制语句(不给规则,不成方圆) [推荐]ORACLE PL/SQL编程详解之三: PL/SQL流程控制语句(不给规则,不成方圆) ——通过知识共享树立个人品牌. 继上五篇: [顶]ORACLE PL/SQL编程详解之二:PL/SQL块结构和组成元素(为山九仞,岂一日之功) [推荐]ORACLE PL/SQL编程之四:把游标说透(不怕做不到,只怕想不到) [推荐]ORACLE PL/SQL编程之五:异常错误处理(知已知彼.百战不