在说核心内容之前,需要问一个问题,既然单个线程的创建和销毁都很简单,我们为什么要使用线程池?
使用池化技术是为了什么?
估计工作过很多年的老鸟们对这些东西都能说出个一二三来,无非就是以下几点:
1、线程相对于进程而言,虽然是轻量级的,但是它的创建依然需要占用我们那点宝贵的内存资源。如果无限制的创建线程,对应垃圾回收而言也是很有压力的,毕竟线程也是对象。使用线程池统一进行线程的调度,便于管理和控制。
2、线程的创建和关闭是需要花费时间的,这点毋庸置疑吧。如果任务非常多,频繁的创建和销毁线程也是需要占用系统的分片时间的,对于系统而言,这点时间对于处理任务的时间来说,有点浪费。
总结一下,使用线程池的目的就是将系统创建的线程进行复用,节约创建和销毁线程带来的时间开销。使用线程池后,创建线程就变成了从线程池获取空闲线程(当然,没有线程的时候也是需要创建的),销毁线程就变成了将线程归还线程池。
创建线程是为了什么?当然是为了处理任务(Task)。
一、JDK对线程池的支持
JDK提供了一套Executor框架,帮助开发人员有效的进行线程控制。
Executor框架结构图(最好自己画个UML类图,首先需要知道类图中每个符号的意思,这是源码必备的分析方法):
以上涉及到的核心成员类都在java.util.concurrent包下。
上图是全部的UML类图关系,猛然一看,估计你不懵都难。所以,我们抛去依赖关系,得到最简图,这样理解起来就不费劲了。
那么,我们说的线程池是谁?就是ThreadPoolExecutor。我学习东西喜欢刨根问底,所以,我想问为什么它就是线程池?我们看上图,所有接口的第一个实现类是谁(不算抽象类)?对头,就是ThreadPoolExecutor。ScheduledThreadPoolExecutor扩展了线程池的功能(为什么英文起名字要见名知意,你从Scheduled是不是明白了什么),可以把它作为扩展功能了解。
以上整个代码架构,有人称之为Executor框架(不要一听框架就觉得有多么神奇高大,它就是个名字,因为线程池的一整套代码组织的起源是Executor,往上看下)。
二、线程池详解
既然要深究线程池,就不得不去看看源码,毕竟源码才是作者表达意思的代码体现。
我们看下线程池有些什么玩意--我们追踪了这个类中的实现,所有的构造函数,无一例外的都归结到下面最长的这个构造函数上,也就是说,这个构造函数是线程池的核心!
/** * Creates a new ThreadPoolExecutor with the given initial * parameters. * * @param corePoolSize the number of threads to keep in the * pool, even if they are idle. * @param maximumPoolSize the maximum number of threads to allow in the * pool. * @param keepAliveTime when the number of threads is greater than * the core, this is the maximum time that excess idle threads * will wait for new tasks before terminating. * @param unit the time unit for the keepAliveTime * argument. * @param workQueue the queue to use for holding tasks before they * are executed. This queue will hold only the Runnable * tasks submitted by the execute method. * @param threadFactory the factory to use when the executor * creates a new thread. * @param handler the handler to use when execution is blocked * because the thread bounds and queue capacities are reached. * @throws IllegalArgumentException if corePoolSize or * keepAliveTime less than zero, or if maximumPoolSize less than or * equal to zero, or if corePoolSize greater than maximumPoolSize. * @throws NullPointerException if workQueue * or threadFactory or handler are null. */ public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueueworkQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) { if (corePoolSize < 0 || maximumPoolSize <= 0 || maximumPoolSize < corePoolSize || keepAliveTime < 0) throw new IllegalArgumentException(); if (workQueue == null || threadFactory == null || handler == null) throw new NullPointerException(); this.corePoolSize = corePoolSize; this.maximumPoolSize = maximumPoolSize; this.workQueue = workQueue; this.keepAliveTime = unit.toNanos(keepAliveTime); this.threadFactory = threadFactory; this.handler = handler; }
我们详细解释下这些参数的含义:
看参数含义,我们最好不要自己去理解,看的时候要结合英文注释,因为注释部分是作者要表达的真正含义。
①corePoolSize: the number of threads to keep in the pool, even if they are idle.(核心线程池大小:为什么这么起名称,是因为作者想表达的含义是线程池应该保证线程数目会有这么多,不管他们创建以后是不是空闲)。
②maximumPoolSize: the maximum number of threads to allow in the pool.(线程池中允许创建线程的最大数目,啥意思?如果核心线程数已经达到,任务还源源不断的来,那么maximumPoolSize表达的意思就是核心线程数+新创建的线程数据<=最大线程数目)。
③keepAliveTime:when the number of threads is greater than the core, this is the maximum time that excess idle threads will wait for new tasks before terminating.(超过核心线程数的线程,在新任务到来之前能存活的最长时间,如果没等到,那么它就拜拜了)
④unit: the time unit for the keepAliveTime argument.(就是keepAliveTime 参数的单位)
⑤BlockingQueue
⑥threadFactory:the factory to use when the executor creates a new thread.(线程工厂,用来创建新线程的工厂,一般使用默认工厂--Executors.defaultThreadFactory(),它是Executors类中的一个静态内部类的方法,返回一个实体类DefaultThreadFactory)
⑦RejectedExecutionHandler handler: the handler to use when execution is blocked because the thread bounds and queue capacities are reached.(当任务队列中任务已经达到了等待执行任务的队列的大小[有界队列会出现]时,要采取什么样的处理(handler)方式,取名叫拒绝策略,就是我应该怎么抛弃你)
下一篇,我们详细介绍下BlockingQueue