需要注意的是,corePoolSize所需的线程并不是立即创建的,需要在提交任务之后进行创建,所以如果有大量的缓存线程数可以先提交一个空任务让线程池将线程先创建出来,从而提升后续的执行效率。
maximumPoolSize
允许的最大线程数。
keepAliveTime
空闲线程空闲存活时间,核心线程需要 allowCoreThreadTimeOut 为true才会退出。
unit
与 keepAliveTime 配合,设置 keepAliveTime 的单位,如:毫秒、秒。
workQueue
线程池中的任务队列。上面提到线程池的主要作用是复用线程来处理任务,所以我们需要一个队列来存放需要执行的任务,在使用池中的线程来处理这些任务,所以我们需要一个任务队列。
threadFactory
当线程池判断需要新的线程时通过线程工程创建线程。
handler
执行被阻止时的处理程序,线程池无法处理。这个与任务队列相关,比如队列中可以指定队列大小,如果超过了这个大小该怎么办呢?JDK已经为我们考虑到了,并提供了4个默认实现。
下列是JDK中默认携带的策略:
AbortPolicy (默认)
抛出 RejectedExecutionException 异常。
CallerRunsPolicy
调用当前线程池所在的线程去执行。
DiscardPolicy
直接丢弃当前任务。
DiscardOldestPolicy
将最旧的任务丢弃,将当前任务添加到队列。
容易混淆的参数:corePoolSize maximumPoolSize workQueue
任务队列、核心线程数、最大线程数的逻辑关系当线程数小于核心线程数时,创建线程。
当线程数大于等于核心线程数,且任务队列未满时,将任务放入任务队列。
当线程数大于等于核心线程数,且任务队列已满
若线程数小于最大线程数,创建线程
若线程数等于最大线程数,调用拒绝执行处理程序(默认效果为:抛出异常,拒绝任务)
那么这三个参数推荐如何设置,有最优值吗?由于java对于协程的支持不友好,所以会大量依赖于线程池和线程。
从而这个值没有最优推荐,需要根据业务需求情况来进行设置。
不同的需求类型可以创建多个不同的线程池来执行。
参考地址:https://github.com/alibaba/p3c
可以看到原因很简单
newSingleThreadExecutor
newFixedThreadPool
在 workQueue 参数直接 使用了 new LinkedBlockingQueue<Runnable>() 理论上可以无限添加任务到线程池。
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(); } public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); }如果提交到线程池的任务由问题,比如 sleep 永久,会造成内存泄漏,最终导致OOM。
同时 阿里还推荐自定义 threadFactory 设置线程名称便于以后排查问题。
问题2:下面的代码输出是什么?应该选C。
虽然最大线程数有100但核心线程数为1,任务队列由100。
满足了 '当线程数大于等于核心线程数,且任务队列未满时,将任务放入任务队列。' 这个条件。
所以后续添加的任务都会被堵塞。
关于 ThreadPoolExecutor 的逻辑在实际使用的时候会有点奇怪,因为线程池中的线程并没有超过最大线程数,有没有一种可能当任务被堵塞很久的时候创建新的线程池来处理呢?
这边推荐大家使用 newWorkStealingPool,也就是ForkJoinPool。采取了工作窃取的模式。
后续会跟大家一起聊聊 ForkJoinPool。