一、序言
上一篇,介绍了线程池的基本工作原理,这里会介绍一些里面的一些饱和策略和几个常用的线程池方法的实现原理。
二、源码分析
线程池极力推荐我们用Executors 提供车的各种工厂,来创建我们的线程池,提供了我们常用的几种创建线程池的方式:
newCachedThreadPool():无界线程池,可以自己进行回收。
newFixedThreadPool(int):固定线程池大小
newSingleThreadExecutor():单个线程
我们来看看里面是如何实现的。
// 无界队列 public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); // 这里我们看看这个构造里面做了什么 public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) { this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), defaultHandler); } corePoolSize = 0 ;核心线程为0 maximumPoolSize: Integer 的最大值 keepAliveTime : 60L,unit 单位是秒 workQueue :SynchronousQueue 阻塞队列 Executors.defaultThreadFactory() :默认工厂,也就是 DefaultThreadFactory defaultHandler :默认饱和策略,也就是 AbortPolicy
下面大概对这里面进行介绍:
2.1 我们可以看出,核心线程数默认为0,最大是int范围最大值,也就是说在没超过这个范围的情况下,所有的线程都是会竞争执行,并且每个线程都只能维持keepAliveTime 时间,这里可以参考前一篇的源码解释,或者跟着源码run.
2.2 SynchronousQueue :单一的阻塞队列,也就是说无论我们是取元素(take),或者存放元素(put),都会阻塞,比如:我们存放了一个对象,那么它就会等待另一个线程将这个对象取出来,才能存放另一个,否则就一直阻塞,同理,如果我们取一个对象,如果里面没有,那么就一直阻塞到另一个线程,存放进去为止。是一个单一的生产消费模式。这里我们暂时不对它进行详细解释.
2.3 DefaultThreadFactory :这玩意儿提供默认创建线程的方式,具体看源码吧!这里这哪是不介绍。
2.4 defaultHandler :这里是默认的AbortPolicy 饱和策略,达到最大线程范围,就抛出异常了。
可能上面的解释不够清晰,下面我们再对几个构造,里面使用的队列以及饱和策略进行介绍:
3 。newFixedThreadPool(int) 固定线程池大小:
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); } // 可以看出,除了制定了核心线程数,以及LinkedBlockingQueue 以外,其他都一样 public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) { this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), defaultHandler); }
3.1 该方法指定了corePoolSize 和 maximumPoolSize的数目,也就是说能保留最低线程数在线程池,而且超过了的线程数只能保存在队列里面,FIFO策略。
3.2 LinkedBlockingQueue :这是基于链表形式的队列,用于存放超出部分的线程。这是一个无界队列。
4. newSingleThreadExecutor():这里是 上面的翻版,仅仅是指定的数目为1而已。
5. 可能大家比较好奇,为啥一个用SynchronousQueue 一个用 LinkedBlockingQueue ?
我的理解是:
5.1 SynchronousQueue 是不能存放多余的线程,也就是说如果我需要10个线程,当第一个开始执行,即使没执行完成,那么后续的线程加入也会从新创建,并执行,并不会加入队列里面。
5.2 LinkedBlockingQueue 是可以存放线程的队列。如果我用newFixedThreadPool,核心线程和最大线程都控制在5个,当前面5个线程为执行完成之前,后面的线程只能被加入队列,不会创建,也就是说必须要等到前面线程执行完成了,才能从队列里面取出执行。
从上面的解释可以看出这个到底怎么使用,当然还有有界队列(ArrayBlockingQueue),优先级队列(PriorityBlockingQueue)等队列,关于这些队列的内部实现,可以参考以前写的关于缓存的淘汰策略。
关于饱和策略的实现,其实也差不多,这里也不暂时不进行源码分析,我copy 了一段介绍,相信能很清楚的解释:
ThreadPoolExecutor.AbortPolicy:表示拒绝任务并抛出异常
ThreadPoolExecutor.DiscardPolicy:表示拒绝任务但不做任何动作
ThreadPoolExecutor.CallerRunsPolicy:表示拒绝任务,并在调用者的线程中直接执行该任务
ThreadPoolExecutor.DiscardOldestPolicy:表示先丢弃任务队列中的第一个任务,然后把这个任务加进队列。
小结:
1.这里本想说明一下线程池的细节,但是大多数围绕了几个队列的知识,里面的实现很多是靠队列本身的实现进行的。
2.关于J.C.U 的一些队列源码的分析和应用,以后分析吧,最近比较忙,分析有问题的地方还请指出,相互学习。
相关推荐
### 线程池 `ThreadPoolExecutor` 原理源码分析 #### 一、概述 线程池作为 Java 并发编程中的重要组件,在实际应用中被广泛使用。其核心类 `ThreadPoolExecutor` 实现了对线程的管理、调度等功能。本文将围绕 `...
《Android应用源码解析——基于SundPoolSample的深度学习》 在当今移动互联网时代,Android作为主流的智能...通过实际操作和源码分析,我们可以将理论知识转化为实战经验,进一步提升我们的编程素养和问题解决能力。
通过分析和学习这些源码,我们可以更深入地理解Java并发库的实际运用,并提升我们的并发编程技能。 总的来说,Java并发库是构建高性能、高并发应用的核心,熟练掌握其原理和实践,对于任何Java开发者来说都是不可或...
9. **源码分析**:博主可能深入分析了Java队列实现的源代码,帮助读者理解其内部工作原理。 总之,Java队列是并发编程和数据结构中的重要概念,理解和熟练运用各种队列类型能够提高程序的效率和并发性能。通过阅读...
在日志记录器的实现中,生产者通常是应用程序的各个部分,它们生成日志事件并将其放入队列。消费者则是专门处理日志的后台线程,负责将队列中的日志事件写入磁盘、发送到远程服务器或者进行其他处理。由于队列的容量...
通过分析`WorkQueue.java`,我们可以学习如何使用工作队列来优化并发程序的性能,比如: 1. **线程池大小优化** - 根据系统资源和任务特性选择合适的线程池大小,避免过度创建线程导致资源浪费。 2. **队列类型选择...
3. 源码分析:如何在Java源码中查找线程池和消息队列的实现。 4. 文件路径管理:如何在Java中管理代码源路径,遍历目录结构。 5. 类和对象:`CodeReader`和`SourcePathManager`类的设计和实现。 为了深入理解这些...
本文将详细讲解如何使用Java中的`ThreadPoolExecutor`来抓取论坛帖子列表,结合源码分析和实用工具的应用。 首先,我们要了解线程池的基本原理。线程池是由一组预先创建的线程组成的,这些线程可以复用,而不是每次...
标签“源码”可能意味着博客还会涉及线程池的内部实现,分析其源代码以理解其工作原理。而“工具”可能指的是使用线程池和工作队列的实用工具或库。 至于压缩包子文件的文件名称,它们看起来与支付接口有关,可能...
"java并发源码分析之实战编程"这个主题深入探讨了Java平台上的并发处理机制,旨在帮助开发者理解并有效地利用这些机制来提高程序性能和可扩展性。在这个专题中,我们将围绕Java并发库、线程管理、锁机制、并发容器...
《backport-util-concurrent_java_backport_源码分析》 backport-util-concurrent是一个Java库,它的主要目的是将Java 5中的并发工具类(java.util.concurrent)回移植到Java 1.3和1.4等早期版本。这个库使得开发者...
1. **线程池**:在`04-Java并发线程池底层原理详解与源码分析-monkey`中,线程池是一个管理线程的高效机制。Java的ExecutorService接口和ThreadPoolExecutor类提供了线程池的实现。线程池可以预先创建一定数量的线程...
本文将深入分析其源码,了解其工作原理和设计思路。 1. **初始化与配置** - UIL的初始化涉及到`ImageLoaderConfiguration`的构建,这包括内存缓存、磁盘缓存、线程池和图片处理策略等设置。`DiskCacheUtils`和`...
这个实践项目“java源码之Thread-threading”聚焦于Android线程的使用,通过学习Flow的教程和Coding上的源代码,我们可以深入理解如何在Android环境中有效地利用线程。下面,我们将详细探讨与线程相关的知识点。 1....
通过以上对 AsyncTask 的源码解析和使用方法的介绍,你应该对如何在 Android 应用中高效地使用异步任务有了更深入的理解。在实际开发中,正确理解和使用 AsyncTask 可以帮助我们编写出更加流畅、响应快速的应用程序...
本资料包"Android应用源码之讲多线程的操作方式.zip"聚焦于Android中多线程的实现方法,通过源码分析,帮助开发者深入理解这一关键概念。 1. **主线程(UI线程)** Android应用默认有一个主线程,负责处理用户界面...
线程池是Java多线程编程中不可或缺的一部分,它通过管理一组可重用线程来提高应用程序的性能和...通过分析这个源码,我们可以深入理解线程池的运作机制,并学习如何根据具体需求调整线程池参数,以实现高效的并发编程。
Java分布式应用是现代企业级软件开发中的重要组成部分,它涉及到多个系统的协同工作,以提高服务的可用性、可扩展性和性能。以下是对标题和描述中所述知识点的详细说明: 1. **系统间通信**: - **基于消息方式**...
RocketMQ是阿里巴巴开源的一款分布式消息中间件,广泛应用于大数据、实时计算、微服务等领域。4.5.2版本是其发展中的一个重要里程碑,...在实际开发中,结合源码分析,能够提升系统性能,增强系统的稳定性和可靠性。
源码分析** 阅读源码可以帮助我们更好地理解线程池的内部机制。例如,`ThreadPoolExecutor.execute()`方法是如何处理任务的提交,`ThreadPoolExecutor.shutdown()`和`shutdownNow()`又是如何优雅地关闭线程池。 **...