# 《并发设计模式》第38章-线程池模式-线程池核心参数解析

作者:冰河
星球:http://m6z.cn/6aeFbs (opens new window)
博客:https://binghe.gitcode.host (opens new window)
文章汇总:https://binghe.gitcode.host/md/all/all.html (opens new window)
源码获取地址:https://t.zsxq.com/0dhvFs5oR (opens new window)

沉淀,成长,突破,帮助他人,成就自我。

  • 本章难度:★★☆☆☆
  • 本章重点:了解线程池的应用场景,重点理解线程池模式的核心原理和应用,以及掌握线程池的核心参数配置,并能够结合自身项目实际场景思考如何将线程池模式灵活应用到自身实际项目中。

大家好,我是冰河~~

在生产环境建议使用ThreadPoolExecutor类创建线程池,使用ThreadPoolExecutor创建线程池时,线程池中的每个参数都可以进行定制,这样不仅可以使线程池中的线程数和任务队列可控,还能在线程池出现性能瓶颈时,根据这些核心参数进行调优,但前提是要对线程池的核心参数有个清晰的认识。

# 一、故事背景

上回说到:基于自定义线程池优化了社区电商系统中优惠券服务推送消息的功能,至此,已经解决了社区电商系统优惠券服务推送消息时,出现的内存占用高和CPU占用高的问题。对于小菜来说,他并不想止步于此,其实在学习之前的并发设计模式的过程中,就不只一次使用到线程池,这次是专门又在学习线程池设计模式,小菜就想彻底搞懂线程池中核心参数的意义。于是经过自己不断的研究,再加上老王的耐心指导,这次算是把线程池的核心参数彻底搞透彻了。

# 二、回滚参数

线程池的参数都是通过ThreadPoolExecutor类的构造方法传递给线程池的,我们再来回滚下线程池的构造方法,如下所示。

public ThreadPoolExecutor(int corePoolSize,
						  int maximumPoolSize,
						  long keepAliveTime,
						  TimeUnit unit,
						  BlockingQueue<Runnable> workQueue) {
	this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
		 Executors.defaultThreadFactory(), defaultHandler);
}

public ThreadPoolExecutor(int corePoolSize,
						  int maximumPoolSize,
						  long keepAliveTime,
						  TimeUnit unit,
						  BlockingQueue<Runnable> workQueue,
						  ThreadFactory threadFactory) {
	this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
		 threadFactory, defaultHandler);
}

public ThreadPoolExecutor(int corePoolSize,
						  int maximumPoolSize,
						  long keepAliveTime,
						  TimeUnit unit,
						  BlockingQueue<Runnable> workQueue,
						  RejectedExecutionHandler handler) {
	this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
		 Executors.defaultThreadFactory(), handler);
}

public ThreadPoolExecutor(int corePoolSize,
						  int maximumPoolSize,
						  long keepAliveTime,
						  TimeUnit unit,
						  BlockingQueue<Runnable> workQueue,
						  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.acc = System.getSecurityManager() == null ?
			null :
			AccessController.getContext();
	this.corePoolSize = corePoolSize;
	this.maximumPoolSize = maximumPoolSize;
	this.workQueue = workQueue;
	this.keepAliveTime = unit.toNanos(keepAliveTime);
	this.threadFactory = threadFactory;
	this.handler = handler;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

可以看到,虽然ThreadPoolExecutor类提供了多个不同的构造方法,但是最终还是调用的参数最多的构造方法来创建线程池对象,如下所示。

public ThreadPoolExecutor(int corePoolSize,
						  int maximumPoolSize,
						  long keepAliveTime,
						  TimeUnit unit,
						  BlockingQueue<Runnable> workQueue,
						  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.acc = System.getSecurityManager() == null ?
			null :
			AccessController.getContext();
	this.corePoolSize = corePoolSize;
	this.maximumPoolSize = maximumPoolSize;
	this.workQueue = workQueue;
	this.keepAliveTime = unit.toNanos(keepAliveTime);
	this.threadFactory = threadFactory;
	this.handler = handler;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

调用上述构造方法时,需要传递七个参数,七个参数的具体含义如下所示。

  • corePoolSize:表示线程池中的核心线程数。
  • maximumPoolSize:表示线程池中的最大线程数。
  • keepAliveTime:表示线程池中的线程空闲时,最多能够保持多久的时间而终止。换句话说,就是当线程池中的线程数量超过corePoolSize时,如果此时没有新的任务提交,核心线程外的线程不会立即销毁,而是需要等待keepAliveTime时间后才会终止。
  • unit:表示keepAliveTime的时间单位。
  • workQueue:表示线程池中的阻塞队列,同于存储等待执行的任务。
  • threadFactory:表示用来创建线程的线程工厂。创建线程池时,会提供一个默认的线程工厂来创建线程,默认的线程工厂创建的线程,会具有相同的优先级,并且是设置了线程名称的非守护线程。
  • handler:表示线程池拒绝处理任务时的策略。如果线程池中的workQueue阻塞队列满了,同时,线程池中的线程数已达到maximumPoolSize,并且没有空闲的线程,此时继续向线程池提交任务,就需要采取某种策略来拒绝任务的执行。

其中,corePoolSize、maximumPoolSize和workQueue三个参数之间的关系如下所示。

(1)当线程池中运行的线程数小于corePoolSize时,如果此时向线程池中提交任务,即使线程池中存在空闲线程,也会直接创建新线程来执行任务。

(2)如果线程池中运行的线程数大于corePoolSize,并且小于maximumPoolSize时,只有当workQueue队列已满时,才会创建新的线程来执行新提交的任务。

(3)如果调用ThreadPoolExecutor类的构造方法时,传递的corePoolSize和maximumPoolSize参数相同,那么创建的线程池的大小是固定的。此时,如果向线程池中提交任务,并且workQueue队列未满时,就会将新提交的任务保存到workQueue队列中,等待空闲的线程,从workQueue队列中获取任务并执行。

(4)如果线程池中运行的线程数大于maximumPoolSize,并且此时workQueue队列已满,则会触发指定的拒绝策略来拒绝任务的执行。

通过ThreadPoolExecutor类创建线程池时,可以使用如下的形式来创建线程池。

new ThreadPoolExecutor(0, 10, 
                       60L, TimeUnit.SECONDS, 
                       new SynchronousQueue<Runnable>());
1
2
3

# 三、深入参数

上一节,还是对线程池的核心参数进行了简单的回顾,本节,我们就深入解析下线程池的核心参数。

(1)corePoolSize参数

corePoolSize参数是线程池中的核心线程数,核心线程是线程池预先分配的线程,当创建线程池时,就会在线程池中分配核心数量的线程,此时,一旦有任务提交过来,核心线程会立即执行任务。corePoolSize参数设置的核心线程如图38-1所示。

# 查看全文

加入冰河技术 (opens new window)知识星球,解锁完整技术文章与完整代码