「这是我参与11月更文挑战的第21天,活动详情查看:2021最后一次更文挑战」
1. 为什么要用线程池
- 降低资源消耗:通过重复利用已创建的线程降低线程创建和销毁造成的消耗
- 提高响应速度:当任务到达时,任务可以不需要等到线程创建就能立即执行。
- 提高线程的可管理性。线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一分配、调优和监控
2. 常用方法
Executors类提供4个静态工厂方法:newCachedThreadPool()、newFixedThreadPool(int)、newSingleThreadExecutor 和 newScheduledThreadPool() ,这些方法最终都是通过ThreadPoolExecutor类来完成的
(1) newCachedThreadPool
- 创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程
- 它比较适合处理执行时间比较小的任务
- corePoolSize为0,maximumPoolSize为无限大,意味着线程数量可以无限大
- keepAliveTime为60S,意味着线程空闲时间超过60S就会被杀死
- 采用SynchronousQueue装等待的任务,这个阻塞队列没有存储空间,这意味着只要有请求到来,就必须要找到一条工作线程处理他,如果当前没有空闲的线程,那么就会再创建一条新的线程
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>());
}
(2) newFixedThreadPool
- 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待
- corePoolSize 和 maximunPoolSize都为用户设定的线程数量nThreads , 因此maximumPoolSize和keepAliveTime将无效
- keepAliveTime为0,意味着一旦有多余的空闲线程,就会被立即停止掉;但这里keepAliveTime无效
- 阻塞队列采用了LinkedBlockingQueue,它是一个无界队列,因此永远不可能拒绝任务
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());
}
(3) newScheduledThreadPool
- 它用来处理延时任务或定时任务
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize,ThreadFactory threadFactory) {
return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
}
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize){
return new ScheduledThreadPoolExecutor(corePoolSize);
}
(4) newSingleThreadExecutor
- 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行
- 创建一个定长线程池,支持定时及周期性任务执行
- 采用的阻塞队列为LinkedBlockingQueue
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>()));
}
3. 注意事项
-
线程资源必须通过线程池体提供,不允许在应用中自行显示创建线程
-
线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理会规避资源耗尽的风险
-
Executors 返回的线程池对象的弊端:
(1) FixedThreadPool 和 SingleThreadpool:
允许的请求队列长度为Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM
(2) CachedThreadPool:
允许的创建线程数量为Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM