创建线程池有哪几种方式?
-
Executors.newFixedThreadPool(4);
- 实际实现
new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())
- 固定线程数为4 同时超出线程数将放在
LinkedBlockingQueue
中 默认是Integer.MAX_VALUE
可能阻塞过多可能出现oom
- 实际实现
-
Executors.newSingleThreadExecutor();
- 实际实现
new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()))
- 固定线程数为1 和newFixedThreadPool 区别包裹了
FinalizableDelegatedExecutorService
这样就无法转成ThreadPoolExecutor
也就不能修改线程数了
- 实际实现
-
Executors.newScheduledThreadPool(4);
- 实际实现
new ScheduledThreadPoolExecutor(corePoolSize);
- 固定线程 返回
可定时
可延时
处理的ScheduledThreadPoolExecutor
ScheduledThreadPoolExecutor extends ThreadPoolExecutor implements ScheduledExecutorService
- 实际实现
-
Executors.newCachedThreadPool();
- 实际实现
new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
- 固定线程数为0 可以无限增长
Integer.MAX_VALUE
会有oom
- 实际实现
-
Executors.newWorkStealingPool();
- 实际实现
new ForkJoinPool (Runtime.getRuntime().availableProcessors(), ForkJoinPool.defaultForkJoinWorkerThreadFactory, null, true)
java.util.concurrent.ForkJoinPool
- 适用于耗时长的线程任务
- 实际实现
-
推荐使用
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)
- 几个参数
-
核心线程池数
- 如果运行的线程少于 corePoolSize,则创建新线程来处理任务,即使线程池中的其他线程是空闲的;
- 如果线程池中的线程数量大于等于 corePoolSize 且小于 maximumPoolSize,则只有当workQueue满时才创建新的线程去处理任务;
- 如果设置的corePoolSize 和 maximumPoolSize相同,则创建的线程池的大小是固定的,这时如果有新任务提交,若workQueue未满,则将请求放入workQueue中,等待有空闲的线程去从workQueue中取任务并处理;
- 如果运行的线程数量大于等于maximumPoolSize,这时如果workQueue已经满了,则通过handler所指定的策略来处理任务;
- 所以,任务提交时,判断的顺序为 corePoolSize –> workQueue –> maximumPoolSize。
-
最大线程数
-
空任务回收时长
-
空任务回收时长单位
-
超出线程池的任务阻塞队列
- 直接切换:这种方式常用的队列是SynchronousQueue
- 使用无界队列:一般使用基于链表的阻塞队列LinkedBlockingQueue。如果使用这种方式,那么线程池中能够创建的最大线程数就是corePoolSize,而maximumPoolSize就不会起作用了(后面也会说到)。当线程池中所有的核心线程都是RUNNING状态时,这时一个新的任务提交就会放入等待队列中
- 使用有界队列:一般使用ArrayBlockingQueue。使用该方式可以将线程池的最大线程数量限制为maximumPoolSize,这样能够降低资源的消耗,但同时这种方式也使得线程池对线程的调度变得更困难,因为线程池和队列的容量都是有限的值,所以要想使线程池处理任务的吞吐率达到一个相对合理的范围,又想使线程调度相对简单,并且还要尽可能的降低线程池对资源的消耗,就需要合理的设置这两个数量
-
线程工厂
- 常用 默认
Executors.defaultThreadFactory()
这种会设定默认线程名 和 普通优先级 和 非守护线程 - 推荐 重写成带线程池名工厂 方便定位问题
- 常用 默认
-
超出策略
- AbortPolicy:直接抛出异常,这是默认策略;
- CallerRunsPolicy:用调用者所在的线程来执行任务;
- DiscardOldestPolicy:丢弃阻塞队列中靠最前的任务,并执行当前任务;
- DiscardPolicy:直接丢弃任务;
-
- 几个参数
线程池都有哪些状态?
源码中状态定义
// runState is stored in the high-order bits
private static final int RUNNING = -1 << COUNT_BITS;
private static final int SHUTDOWN = 0 << COUNT_BITS;
private static final int STOP = 1 << COUNT_BITS;
private static final int TIDYING = 2 << COUNT_BITS;
private static final int TERMINATED = 3 << COUNT_BITS;