聊聊线程池
一个通用的线程池需要提供以下4种能力,使用方根据业务场景选择适合的策略:
1.可自定义的线程数
2.回收线程的策略
3.任务队列堆积的策略
4.处理不过来时的拒绝策略
Executors里面封装了这几种比较常用的线程池,newFixedThreadPool、newSingleThreadExecutor、newCachedThreadPool、newFixedThreadPool延迟版、newSingleThreadExecutor延迟版
1.ThreadPoolExecutor的关键参数
newFixedThreadPool、newSingleThreadExecutor、newCachedThreadPool都是对ThreadPoolExecutor的封装,延迟版的线程池是对ScheduledThreadPoolExecutor的封装,那了解ThreadPoolExecutor和ScheduledThreadPoolExecutor的参数也就能明白这几种线程池的工作原理了。
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.corePoolSize = corePoolSize; this.maximumPoolSize = maximumPoolSize; this.workQueue = workQueue; this.keepAliveTime = unit.toNanos(keepAliveTime); this.threadFactory = threadFactory; this.handler = handler; }
corePoolSize |
可存活的线程数,这部分线程即使空闲也不会被回收,除非设置allowCoreThreadTimeOut为true |
maximumPoolSize |
最大存活的线程数 |
workQueue |
堆积任务的队列 |
keepAliveTime |
超过corePoolSize那部分的线程,在等待指定时间后仍没有可执行的任务,则回收线程 |
threadFactory |
线程池工厂,可以设置线程名称等操作 |
handler |
当可用线程为0并且队列满了的时候,会调用handler的rejectedExecution方法 |
2.自带的几种拒绝执行策略
AbortPolicy | 抛出异常 |
CallerRunsPolicy | 使用调用者的线程执行该任务,即submit方法变成了同步操作 |
DiscardPolicy | 丢弃该任务 |
DiscardOldestPolicy | 丢弃最老的任务,重新提交该任务 |
3.Executors线程池的配置和特点
3.1 固定线程数,无边界队列,适合IO密集的运算
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
活跃线程数最多为nThreads个,
线程调用队列的take方法阻塞获取任务,不回收线程
LinkedBlockingQueue队列大小为Integer的最大值,堆积的任务超过这个值并且可用线程数为0,就抛出RejectedExecutionException
3.2 一个线程,无边界队列,适合cpu密集的运算
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); }
与newFixedThreadPool类似,活跃线程为1,并且用FinalizableDelegatedExecutorService做代理,使得不能通过setMaximumPoolSize等方法修改线程池,同时重写了finalize方法,使得在线程池被GC回收的时候关闭线程池。
3.3 处理不过来就创建线程,无消息堆积的能力
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }
活跃线程最大值为Integer最大值
线程调用队列的poll阻塞60秒获取任务,超时则回收线程
SynchronousQueue队列大小为1,无任务堆积的能力,即可用线程数为0时则抛出RejectedExecutionException
4.扩展点
protected void terminated() { } protected void afterExecute(Runnable r, Throwable t) { } protected void terminated() { }
5.ScheduledThreadPoolExecutor的关键参数
public ScheduledThreadPoolExecutor(int corePoolSize, ThreadFactory threadFactory, RejectedExecutionHandler handler) { super(corePoolSize, Integer.MAX_VALUE, 0, TimeUnit.NANOSECONDS, new DelayedWorkQueue(), threadFactory, handler); }
corePoolSize
|
可存活的线程数,这部分线程即使空闲也不会被回收,如果设置allowCoreThreadTimeOut为true会抛异常 |
threadFactory
|
线程池工厂,可以设置线程名称等操作 |
handler
|
当可用线程为0并且队列满了的时候,会调用handler的rejectedExecution方法 |
虽然ScheduledThreadPoolExecutor是继承了ThreadPoolExecutor,但是线程池的线程数最大为corePoolSize,设置maximumPoolSize是无效的。
6.延迟和定期执行的方法
public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit);
延迟指定时间执行该任务,只执行一次
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit);
延迟指定时间执行该任务,并按提交任务的时候算起,固定的时间间隔定期执行。如果线程数不够的时候,则排队等待,不会被多个线程并发执行。
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit);
延迟指定时间执行该任务,并按上一次完成任务的时候算起,固定的时间间隔定期执行。如果线程数不够的时候,则排队等待,不会被多个线程并发执行。