ThreadPoolExecutor构造方法
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
构造方法参数说明
- corePoolSize
核心线程数,默认情况下核心线程会一直存活,即使处于闲置状态也不会受存keepAliveTime限制。除非将allowCoreThreadTimeOut设置为true。 - maximumPoolSize
线程池所能容纳的最大线程数。超过这个数的线程将被阻塞。当任务队列为没有设置大小的LinkedBlockingDeque时,这个值无效。 - keepAliveTime
非核心线程的闲置超时时间,超过这个时间就会被回收。 - unit
指定keepAliveTime的单位,如TimeUnit.SECONDS。当将allowCoreThreadTimeOut设置为true时对corePoolSize生效。 - workQueue
线程池中的任务队列.
常用的有三种队列,SynchronousQueue,LinkedBlockingDeque,ArrayBlockingQueue。 - threadFactory
ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
.setNameFormat("name-pool-%d").setDaemon(true).build();
可以按照此方法定制threadFactory
-
RejectedExecutionHandler
当线程池中的资源已经全部使用,添加新线程被拒绝时,会调用RejectedExecutionHandler的rejectedExecution方法。
new ThreadPoolExecutor.AbortPolicy()
线程池规则
线程池的线程执行规则跟任务队列有很大的关系。
当任务队列没有大小限制时:
- 如果线程数量<=核心线程数量,那么直接启动一个核心线程来执行任务,不会放入队列中。
- 如果线程数量>核心线程数,但<=最大线程数,并且任务队列是LinkedBlockingDeque的时候,超过核心线程数量的任务会放在任务队列中排队。
- 如果线程数量>核心线程数,但<=最大线程数,并且任务队列是SynchronousQueue的时候,线程池会创建新线程执行任务,这些任务也不会被放在任务队列中。这些线程属于非核心线程,在任务完成后,闲置时间达到了超时时间就会被清除。
- 如果线程数量>核心线程数,并且>最大线程数,当任务队列是LinkedBlockingDeque,会将超过核心线程的任务放在任务队列中排队。也就是当任务队列是LinkedBlockingDeque并且没有大小限制时,线程池的最大线程数设置是无效的,他的线程数最多不会超过核心线程数。
- 如果线程数量>核心线程数,并且>最大线程数,当任务队列是SynchronousQueue的时候,会执行线程池的拒绝策略。
当任务队列大小有限制时:
- 当LinkedBlockingDeque塞满时,新增的任务会直接创建新线程来执行,当创建的线程数量超过最大线程数量时会执行线程池的拒绝策略。
- SynchronousQueue没有数量限制。因为他根本不保持这些任务,而是直接交给线程池去执行。当任务数量超过最大线程数时会执行线程池的拒绝策略。
LinkedBlockingDeque:
ExecutorService executor=new ThreadPoolExecutor(8,16,0, TimeUnit.DAYS,new LinkedBlockingDeque<>(20),threadFactory,new ThreadPoolExecutor.AbortPolicy());
当线程数为8时,全部交由核心线程完成,当线程超过8时,放到队列里等待创建线程,当超过队列大小时,也就是有28个线程时,将会直接创建线程,当总线程数超过16时,也就是当等待队列和执行中线上加起来超过36(20+16)时,将会执行线程池的拒绝策略。
SynchronousQueue:
ExecutorService executor=new ThreadPoolExecutor(8,16,0, TimeUnit.DAYS,new SynchronousQueue<>(),threadFactory,new ThreadPoolExecutor.AbortPolicy());
当线程数为8时,全部交由核心线程完成,当线程超过8时,将会直接创建线程,当总线程数超过16时,将会执行线程池的拒绝策略。
个人推荐的线程池构造
一、用LinkedBlockingDeque,CallerRunsPolicy
- 一般建议用CallerRunsPolicy,我们都不希望把任务丢失,但是如果实在是微不足道的任务,也可以抛出异常,但是要做好异常捕捉后的工作。除此以外,还有一个最佳实践是自己定义一个拒绝策略去实现RejectedExecutionHandler接口,实现rejectedExecution方法,完成重试等功能。
- LinkedBlockingDeque的好处就是可以有一个队列可以缓冲,不至于全部以最大线程数去执行。
- 下面这个线程池用于CPU密集型的,如果是IO密集型的话,可以把核心线程改成16,甚至更大(根据实际需要),最大线程数改成32,也不建议太大,那样会占用太多的线程数。
private static final ExecutorService pool = new ThreadPoolExecutor(8, 16, 0, TimeUnit.SECONDS,
new LinkedBlockingDeque<>(1024),
new ThreadFactoryBuilder().setNameFormat("message-pool-%s").build(),
new ThreadPoolExecutor.CallerRunsPolicy());
二、用SynchronousQueue,CallerRunsPolicy
SynchronousQueue的好处在于不占用队列空间,直接由线程池运行,缺点在于少了缓冲的空间。下面这个还是CPU密集型的,如果是IO密集型参考上面。
private static final ExecutorService pool = new ThreadPoolExecutor(8, 16, 0, TimeUnit.SECONDS,
new SynchronousQueue<>(),
new ThreadFactoryBuilder().setNameFormat("message-pool-%s").build(),
new ThreadPoolExecutor.CallerRunsPolicy());
线程池的实现原理
ThreadPoolExecutor类里有一个线程集合HashSet
那么非核心线程是如何做到超时退出的呢?
其实我们可以注意下,任务是存在队列里的,队列的poll方法可以设置超时时间,这样我们去队列里获取任务的时候如果超时了就会返回一个失败的状态,这样如果在设置的超时时间范围内非核心线程获取不到任务就会自动退出并销毁。
具体是在源码的getTask()方法里:
try {
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
标签:LinkedBlockingDeque,队列,任务,线程,new,ThreadPoolExecutor
From: https://www.cnblogs.com/leecoder5/p/18420780