常用线程池(4种)
核心参数
- int corePoolSize:线程池中 核心线程数最大值。
- int maximumPoolSize:线程池中 线程总数最大值。
- long keepAliveTime:非核心线程闲置超时时间。
- TimeUnit unit:keepAliveTime的时间单位。
- BlockingQueue workQueue(非必须):阻塞队列,维护着等待执行的Runnable任务对象。
- LinkedBlockingQueue:链式阻塞队列,底层数据结构是链表,默认大小Integer.MAX_VALUE,也可以指定队列大小。
- ArrayBlockingQueue:数组阻塞队列,底层数据结构是数组,需要指定队列的大小。
- SynchronousQueue:同步队列,内部容量为0,每个put操作必须等待一个take操作,反之亦然。
- DelayQueue:延迟队列,该队列中的元素只有当其指定的延迟时间到了,才能从队列中获取到该元素。
- ThreadFactory threadFactory (非必须):创建线程的工厂,用于批量创建线程,统一在创建线程的时候设置一些参数(如:是不是守护线程,线程的优先级),不指定,创建默认的线程工厂。
- RejectedExecutionHandler handler:拒绝处理策略,线程数量 > 最大线程数量时,就会采用拒绝处理策略。
- ThreadPoolExecutor.AbortPolicy:默认拒绝策略,丢弃任务并抛出RejectedExecutionException异常。
- ThreadPoolExecutor.DiscardPolicy:丢弃新来的任务,不抛异常。
- ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列头部(最旧的)的任务,然后重新尝试执行程序,如果再次失败,重复此过程。
- ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务。
newCachedThreadPool
可缓存线程池;
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
运行流程:
- 提交任务进线程池。
- corePoolSize为0,不创建核心线程,线程池最大为Integer.MAX_VALUE。
- 尝试将任务添加到SynchronousQueueduilie。
- 如果SynchronousQueue入列成功,等待被当前运行的线程空闲后拉取执行;如果当前没有空闲线程,那么就创建一个非核心线程,然后从SynchronousQueue队列拉取任务并在当前线程执行。
- 如果SynchronousQueue队列已经有任务在等待,入列操作将被阻塞。
概述:
当需要执行很多短时间的任务,newCachedThreadPool线程池的线程复用率比较高,会显著的提升性能。而且线程60秒后会回收,意味着即使没有任务进来,newCachedThreadPool也不会占用很多资源。
缺点:
线程池中的线程数最大数是Integer.MAX_VALUE,可能会创建非常多的线程,几乎不会触发拒接策略,甚至造成OOM。
newFixedThreadPool
定长线程池;
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
特点:
- 因为corePoolsize 等于maximumPoolSize (核心线程数量和总线程数量相等),所以newFixedThreadPool只会创建核心线程。
- 在getTask()方法,如果队列里没有任务可取,线程会一直阻塞在LinkedBlockingQueue.take(),线程不会被回收。
- 由于线程不会被回收,会一直卡在阻塞,所以在没有任务的情况下,占用的资源多。
- 因为阻塞队列可以很大(最大值为Integer最大值),所以几乎不会触发拒绝策略。
概述:
核心线程数量和总线程数量相等,所以只能创建核心线程,不能创建非核心线程。因为LinkedBlockingQueue队列的默认大小是Integer.MAX_VALUE,所以如果核心线程空闲,则交给核心线程处理;如果核心线程不空闲,则入列等待,直到核心线程空闲。
缺点:
堆积的请求进入阻塞队列可能会消耗非常大的内存,甚至造成OOM。
newSingleThreadExecutor
单线程的线程池
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
特点:
- 有且仅有一个核心线程corePoolsize = maximumPoolSize = 1。
- 使用了LinkedBlockingQueue队列(容量是Integer.MAX_VALUE)。
- 先来先执行的顺序执行
概述:
有且仅有一个核心线程corePoolsize = maximumPoolSize = 1。使用了LinkedBlockingQueue队列(容量是Integer.MAX_VALUE),容量很大,不会创建非核心线程,并且任务是按照先进来先执行的顺序执行的,当这个唯一线程非空闲的时候,新的任务存储在阻塞队列中。
缺点:
因为使用的LinkedBlockingQueue队列容量太大,不容易触发拒绝策略,容易造成OOM。
newScheduledThreadPool
定长线程池,支持定时以及周期性
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize
return new ScheduledThreadPoolExecutor(corePoolSize);
}
//ScheduledThreadPoolExecutor():
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE,
DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
new DelayedWorkQueue());
}
为什么要使用线程池?
- 控制并发数量
- 可以复用已经创建的线程(主要原因)
- 主要还是因为为了性能,线程的创建涉及cpu状态的切换,如果线程能被复用会大大降低这种状态切换带来的性能损耗
- 对线程统一管理