核心线程池的内部实现

  依然参考 JDK 对线程池的支持,各个接口、相关类之间的关系: 

(1)对于Executors中几个创建线程池方法底层实现:

// 创建固定线程数量的线程池
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>());
}

  由以上线程池的实现代码可以看到,它们都只是 ThreadPoolExecutor 类的封装。为何 ThreadPoolExecutor 有如此强大的功能呢?来看一下 ThreadPoolExecutor 最重要的构造函数:

public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)

(2)ThreadPoolExecutor 核心构造函数的参数含义如下:

  1)coolPoolSize:指定了线程池中的线程数量;

  2)maximumPoolSize:指定了线程池中的最大线程数量;

  3)keepAliveTime:当线程池线程数量超过 corePoolSize 时,多余的空闲线程的存活时间。即:超过 corePoolSize 的空闲线程,在多长时间内会被销毁;

  4)unit:keepAliveTime 的单位;

  5)workQueue:任务队列,被提交但未被执行的任务;

  6)threadFactory:线程工厂,用户创建线程,一般使用默认的即可;

  7)handler:拒绝策略。当任务太多来不及处理,如何拒绝任务执行。

  以上参数中,大部分都很简单,只有 workQueue 和 handler 需要进行详细说明!

(3)workQueue 任务队列详解

  参数 workQueue 指被提交但未执行的任务队列,它是一个 BlockingQueue 接口的对象,仅用于存放 Runnable 实例对象。根据队列功能分类,在 ThreadPoolExecutor 的构造函数中可使用一下几种 BlockingQueue

  1)直接提交的队列:该功能由 SynchronousQueue 对象提供。SynchronousQueue 是一个特殊的 BlockingQueue。SynchronousQueue 没有容量,每一个插入操作都要等待一个相应的删除操作,反之,每一个删除操作都要等待对应的插入操作。如果使用 SynchronousQueue,提交的任务不会被真实的保存,而总是将新任务提交给线程执行,如果没有空闲的线程,则尝试创建新的线程,如果线程数量已经达到最大值,则执行拒绝策略。因此,使用 SynhronousQueue 队列,通常要设置很大的 maximumPoolSize 值,否则很容易执行拒绝策略。

  2)有界的任务队列:有界的任务队列可以使用 ArrayBlockingQueue 实现。ArrayBlockingQueue 的构造函数必须带一个容量参数,表示该队列的最大容量,如下所示:

public ArrayBlockingQueue(int capacity)

  当使用有界的任务队列时,若有新的任务需要执行,如果线程池的实际线程数小于 corePoolSize,则会优先创建新的线程,若大于 corePoolSize,则会将新任务加入等待队列。若等待队列已满,无法加入,则在总线程数不大于 maximumPoolSize 的前提下,创建新的线程执行任务。若大于 maximumPoolSize,则执行拒绝策略。可见,有界队列仅当在任务队列装满时,才可以将线程数提升到 corePoolSize 以上,换言之,除非系统非常繁忙,否则确保核心线程数维持在 corePoolSize。

  3)无界的任务队列:无界任务队列可以通过 LinkedBlockingQueue 类实现。与有界队列相比,除非系统资源耗尽,否则无界队列不存在入队失败的情况。当有新的任务到来,系统的线程数小于 corePoolSize 时,线程池会创建新的线程执行任务,但当系统的线程数达到 corePoolSize 后,就不会继续增加。若后续仍有新的任务加入,而又没有空闲的线程资源,则任务直接进入队列等待。若任务创建和处理的速度差异很大,无界队列会保持快速增长,直到耗尽系统内存。

  注意:LinkedBlockingQueue无界的任务队列,是一个单向链表!线程池的初始化容量为:Integer.MAX_VALUE

  4)优先任务队列:优先任务队列是带有执行优先级的队列。它通过 PriorityBlockingQueue 实现,可以控制任务的执行先后顺序。它是一个特殊的无界队列。无论是有界队列 ArrayBlockingQueue,还是未指定大小的无界队列 LinkedBlockingQueue,都是按照先进先出算法处理任务的。而 PriorityBlockingQueue 则可以根据任务自身的优先级顺序先后执行,在确保系统性能的同时,也能有很好的质量保证(总是确保高优先级的任务先执行)。

(4)这里给出 ThreadPoolExecutor 线程池的核心调度代码,这段代码也充分体现了上述线程池的工作逻辑

  

(5)拒绝策略

  ThreadPoolExecutor 的最后一个参数指定了拒绝策略。也就是当任务数量超过系统实际承载能力时,该如何处理?拒绝策略可以说是系统超负荷运行时的补救措施,通常由于压力太大而引起的,也就是线程池中的线程已经用完了,无法继续为新任务服务,同时,等待队列中也已经排满了,再也塞不下新任务了。这时,我们就需要一套机制合理地处理这个问题。

  JDK内置提供了四种拒绝策略:

  1)AbortPolicy 策略:该策略会直接抛出异常,阻止系统正常工作;

  2)CallerRunsPolicy 策略:只要线程池未关闭,该策略直接在调用者线程中,运行当前被丢弃的任务。显然这样做不会真的丢弃任务,但是任务提交线程的性能极有可能会急剧下降;

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

  4)DiscardPolicy 策略:该策略默默地丢弃无法处理的任务,不予任何处理。如果允许任务丢弃,我觉得这可能是最好的一种方案吧!

  

  以上就是线程池的详细解释,写了近两天,终于理清楚了,这下面试就可以侃侃而谈了,欢迎大家学习一起进步!

Java编发编程 - 线程池的认识(二)的更多相关文章

  1. Java编发编程 - 线程池的认识(一)

    每逢面试都会询问道线程池的概念和使用,但是工作中真正的又有多少场景使用呢?相信大家都会有这样的疑问:面试选拔造汽车,实际进公司就是拧螺丝!但是真正要把这颗螺丝拧紧,拧牢,没有这些最底层的知识做铺垫你可 ...

  2. Java并发编程——线程池的使用

    在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统 ...

  3. Java 并发编程 | 线程池详解

    原文: https://chenmingyu.top/concurrent-threadpool/ 线程池 线程池用来处理异步任务或者并发执行的任务 优点: 重复利用已创建的线程,减少创建和销毁线程造 ...

  4. Java并发编程——线程池

    本文的目录大纲: 一.Java中的ThreadPoolExecutor类 二.深入剖析线程池实现原理 三.使用示例 四.如何合理配置线程池的大小 一.Java中的ThreadPoolExecutor类 ...

  5. Java并发编程--线程池

    1.ThreadPoolExecutor类 java.uitl.concurrent.ThreadPoolExecutor类是线程池中最核心的一个类,下面我们来看一下ThreadPoolExecuto ...

  6. JAVA 并发编程-线程池(七)

    线程池的作用: 线程池作用就是限制系统中运行线程的数量. 依据系统的环境情况.能够自己主动或手动设置线程数量,达到运行的最佳效果:少了浪费了系统资源,多了造成系统拥挤效率不高.用线程池控制线程数量,其 ...

  7. java并发编程-线程池的使用

    参考文章:http://www.cnblogs.com/dolphin0520/p/3932921.html 深入剖析线程池实现原理 将从下面几个方面讲解: 1.线程池状态 2.任务的执行 3.线程池 ...

  8. Java多线程与线程池技术

    一.序言 Java多线程编程线程池被广泛使用,甚至成为了标配. 线程池本质是池化技术的应用,和连接池类似,创建连接与关闭连接属于耗时操作,创建线程与销毁线程也属于重操作,为了提高效率,先提前创建好一批 ...

  9. java笔记--使用线程池优化多线程编程

    使用线程池优化多线程编程 认识线程池 在Java中,所有的对象都是需要通过new操作符来创建的,如果创建大量短生命周期的对象,将会使得整个程序的性能非常的低下.这种时候就需要用到了池的技术,比如数据库 ...

随机推荐

  1. linux 压缩 tar命令

    linux中tar命令用法    总结 *.tar 用 tar –xvf 解压 *.gz 用 gzip -d或者gunzip 解压 *.tar.gz和*.tgz 用 tar –xzf 解压 *.bz2 ...

  2. Redis Lua脚本完全入门

    1. 前言 Redis是高性能的KV内存数据库,除了做缓存中间件的基本作用外还有很多用途,比如胖哥以前分享的Redis GEO地理位置信息计算.Redis提供了丰富的命令来供我们使用以实现一些计算.R ...

  3. py正则表达式(全是干货系列)

      正则表达式的作用在这里不多赘述了,反正处理文本任务贼六就对了.Python中的正则表达式是内置在re模块中的,我们就对这个模块进行详细地讲解.这是一篇媲美帮助文档的文章!对就这么自信,不服你顺着网 ...

  4. Aspose.Words实现邮件合并功能和打印

    前言 最近公司要做一个B/S架构的web打印系统,主要是可以上传.下载.邮件合并.打印等等,还有就是角色的分配.用户的创建.日志记录等等,跟一般的web系统一样.可能不一样的就是需求:想把excel的 ...

  5. 群晖DS218+做maven私服(nexus3)

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  6. 01 . Go框架之Gin框架从入门到熟悉(路由和上传文件)

    Gin框架简介 Gin是使用Go/Golang语言实现的HTTP Web框架, 接口简洁, 性能极高,截止1.4.0版本,包含测试代码,仅14K, 其中测试代码9K, 也就是说测试源码仅5k左右, 具 ...

  7. 【总结】springcloud

    一.spirngcloud概述 1.springcloud是什么? spring cloud是一个一站式的开发分布式系统的框架,为开发人员提供了快速构建分布式系统中一些常见模式的工具(如:配置管理,服 ...

  8. drf 权限校验设置与源码分析

    权限校验 权限校验和认证校验必须同时使用,并且权限校验是排在认证校验之后的,这在源码中可以查找到其执行顺序. 权限校验也很重要,认证校验可以确保一个用户登录之后才能对接口做操作,而权限校验可以依据这个 ...

  9. Kubernetes Pod驱逐策略

    Kubelet 能够主动监测和防止计算资源的全面短缺. 在资源短缺的情况下,kubelet 可以主动地结束一个或多个 Pod 以回收短缺的资源. 当 kubelet 结束一个 Pod 时,它将终止 P ...

  10. Python3网络学习案例二:traceroute详解

    1. 写在前面 本文是基于上一篇"ping详解"写的: 不同操作系统下的命令也不同,本文仅针对windows系统,命令为"tracert xxx",效果如下 2 ...