Java线程池相关
1.参数释义
Executor接口的实现类:ThreadPoolExecutor
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler);
corePoolSize :线程池的核心线程数量,核心线程即使在没有用的时候,也不会被回收;
maximumPoolSize :线程池中的最大线程数量;
keepAliveTime :线程池中除了核心线程之外的其他线程最长可以保留的时间;
util :计算这个时间的单位;
workQueue :等待队列,任务可以储存在任务队列中等待被执行,按照FIFO(先进先出)原则执行;
threadFactory :创建线程的线程工厂;
handler :拒绝策略,在任务满了之后,拒绝执行某些任务。
2.执行流程
任务进来时,首先判断核心线程是否处于空闲状态,如果有空闲,核心线程就先执行任务,如果核心线程已满,则判断任务队列是否有地方存放该任务,如果有,就将任务保存在任务队列中,等待执行,如果满了,再判断最大可容纳的线程数,如果没有超出这个数量,就开创非核心线程执行任务,如果超出了,就调用handler实现拒绝策略。
3.handler的拒绝策略
handler的拒绝策略有四种:
AbortPolicy: 不执行新任务,直接抛出异常,提示线程池已满;
DisCardPolicy: 不执行新任务,也不抛出异常;
DisCardOldSetPolicy: 将消息队列中的第一个任务替换为当前新进来的任务执行;
CallerRunsPolicy: 直接调用execute执行当前任务。
4.五种种线程池
newCachedThreadPool(): 创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程;最大线程数为Integer.MAX_VALUE,容易造成内存泄漏。
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
newFixedThreadPool(): 创建定长线程池,可控制最大并发数,超出的线程在队列种等待;不会复用线程,每运行一个Runnable都会通过ThreadFactory创建一个线程;如果定长的数量设置的过小,有可能造成任务堆积 ,最好根据系统资源进行设置,如Runtime.getRuntime().availableProcessors()
;没有设置超时时间,如果未关闭线程池,可能导致内存泄漏。
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory);
}
newScheduledThreadPool(): 创建定长线程池,支持定时及周期性任务执行。设计思想是:每一个被调度的任务都会由线程池中一个线程去执行,因此任务是并发执行的,相互之间不会受到干扰。只有当任务的执行时间到来时,才会真正启动一个线程,其余时间都是在轮询任务的状态。
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize, ThreadFactory threadFactory) {
return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
}
public ScheduledThreadPoolExecutor(int corePoolSize, ThreadFactory threadFactory) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedWorkQueue(), threadFactory);
}
newSingleThreadExecutor(): 创建单线程化的线程池,只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO,LIFO,优先级)执行。
public static ExecutorService newSingleThreadExecutor() {
return new Executors.FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
newWorkStealingPool(): jdk1.8新增,根据所需要的并行层次动态创建和关闭线程,通过使用多个队列减少竞争,底层用ForkJoinPool
实现,可以充分利用多核cpu的优势,把一个任务拆分成多个小任务,多个小任务在多个处理器上并行执行,都执行完之后,再将这些执行结果合并起来。
public static ExecutorService newWorkStealingPool() {
return new ForkJoinPool
(Runtime.getRuntime().availableProcessors(),
ForkJoinPool.defaultForkJoinWorkerThreadFactory,
null, true);
}
5.关闭线程池
线程池自动关闭的两个条件:线程池的引用不可达,线程池中没有线程。
如果没有关闭线程池,有可能导致内存泄漏。
关闭线程池可以调用shutdown()
,shutdownNow()
方法。
关闭线程池操作时有几个问题:
1.是否可以继续执行新任务?继续提交新任务会怎样?
当线程池关闭后,继续提交新任务会执行拒绝策略,默认的拒绝策略是AbortPolicy
,会抛出异常。
2.等待队列里的任务是否还会执行?
用shutdown()
方法关闭线程池,队列中的任务仍然会继续执行;如果希望等待队列中的任务不继续执行,可以使用shutdownNow()
方法。
3.正在执行的任务是否会立即中断?
正在执行的任务是否会立即中断,首先要理解线程中的interrupt()
方法,它在线程中设置一个中断标志,如果该线程被阻塞的话,则抛出异常InterruptedException
。
调用shutdown()
方法,线程池被关闭,正在运行的任务没有被中断;
调用shutdownNow()
方法,正在运行的任务可以被中断。