Java多线程——线程池

本文详细解析了Java线程池ThreadPoolExecutor的构造函数、运作机制(线程创建、工作队列、饱和策略)、线程工厂以及如何进行线程池的扩展。重点介绍了核心线程数、最大线程数、保持空闲时间、队列类型、拒绝策略和自定义线程创建方式,同时阐述了无界队列、有界队列、同步移交队列的特性及饱和策略的实现,旨在帮助开发者更好地理解和利用线程池提高程序并发性能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、ThreadPoolExecutor接口

之前提到过Executors所提供的四种线程池,即:Scheduled,Single,Fixed,Cached。如果这几种线程池不能完全满足你的需求,那么通过ThreadPoolExecutor你也可以创建并且定制合适的线程池。


ThreadPoolExecutor常用的构造函数:

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

corePoolSize: 线程池的目标大小。

workQueue:工作队列,新的任务会被加进队列里面。

maximumPoolSize:线程池最多允许的线程数量。

keepAliveTime:当线程空闲时间超过这个值的时候,就会被标记为可回收。

threadFactory:线程工厂,定义创建线程的方式。

handler:定义发生拒绝处理之后的后续操作,与饱和策略有关。

之前提到的四种线程池都是ThreadPoolExecutor参数的一些特殊情况,如corePoolSize = maximumPoolSize。


二. ThreadPoolExecutor的运作机制

1. 线程的创建

首先,在线程池启动初期,线程池中并不会立即启动线程,只有当有任务提交时才会有线程被创建(可以通过调用prestartAllCoreThreads来使线程池在初期阶段就达到目标大小)。当请求到达的速率超过了处理的速率时,新的线程会不断地被创建,直到达到了线程池的目标大小。


2. 工作队列

当还有新的请求达到,而线程池已经达到了固定的大小的时候,新的请求就会堆积在workQueue里面。workQueue具体可以有三种实现:无界,有界和同步移交。

a. 无界队列

当任务的处理速率大于到达速率的时候,如果是无界的workQueue,workQueue会一直的增大。默认情况下newFixedThreadPool和newSingleThreadExecutor都是使用的无界的linkedBlockingQueue。但是,无界的队列可能会由于新的任务不停地过快的到来而持续增加。

b. 有界队列

如果使用有界的workQueue的话,就可以更加稳妥,而当有界队列也满了之后,线程池就会创建新的线程,直到达到maximumPoolSize。

c. 同步移交(SynchronousQueue)

当线程池是无界的时候或者在线程池可以拒绝任务时才可以用同步移交(否则线程池会爆掉)。该方式可以避免任务排队,但需要一个非常大的(无界的)线程池来做支持,如果线程池规模太小,就会频繁地拒绝任务。


3. 饱和策略

当工作队列被填满了之后就会使用饱和策略,即参数中的handler。setRejectedExecutorHandler可以用来修改饱和策略。JDK所提供的饱和策略包括AbortPolicy,CallerRunsPolicy,DiscardPolicy和DiscardOldestPolicy。

DiscardPolicy会悄悄地放弃新任务,DiscardOldestPolicy会放弃下一个即将被执行的任务,然后尝试提交新的任务(当workQueue为priorityQueue时,放弃下一个即将被执行的任务并不意味着会放弃最旧的任务)。

Abort会抛出未检查的RejectedExecutionException。

Caller-run是一种调节机制。它会把一部分任务退回到主线程中执行,这样也可以降低新的任务到来的速率(主线程正忙于处理退回的任务,无暇接受新的任务)。当主线程不接受新的任务的时候新的请求就会在TCP层中堆积。当TCP层的队列也满了之后就会抛弃请求。最终实现平缓的性能降低。


4. 线程工厂

线程工厂是线程池用来创建线程的。

public interface ThreadFactory {
	Thread newThread(Runnable r);
}

我们可以自己实现线程工厂,从而实现一些定制的行为。


三、扩展ThreadPoolExecutor

ThreadPoolExecutor的beforeExecutor,afterExecutor和terminated方法是可以被重写的。看到beforeExecutor和afterExecutor我们很自然地会想到日志,统计之类的计划任务。terminated用来在线程池关闭之后释放资源。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值