线程池的概念
简单来说线程池就是一个管理线程的池子,当我们有任务需要执行无需创建线程,只需要将任务提交到线程池即可。
线程池的优点
- 降低系统资源消耗,通过重用已存在的线程,降低线程创建和销毁造成的消耗。
- 提高系统响应速度,当有任务到达时,通过复用已存在的线程,无需等待新线程的创建便能立即执行。
- 方便线程并发数的管控。因为线程若是无限制的创建,可能会导致内存占用过多而产生OOM,并且会造成cpu过度切换(cpu切换线程是有时间成本的(需要保持当前执行线程的现场,并恢复要执行线程的现场))。
- 提供更强大的功能,延时定时线程池。
线程池的创建
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), handler);
}
核心参数的作用
- corePoolSize: 线程池核心线程数最大值,核心线程是常驻线程即使没有可执行的任务也不会被回收。
- maximumPoolSize: 线程池最大线程数大小。
- keepAliveTime: 线程池中非核心线程空闲的存活时间大小。
- 线程空闲存活时间单位
- 存放任务的阻塞队列
- 用于设置创建线程的工厂,可以给创建的线程设置有意义的名字,可方便排查问题。
- 线城池的饱和策略事件,主要有四种类型。
线程池运行过程
线程池运行过程- 当我们向线程池提交任务时,线程池会判断存活的核心线程数是否小于线程数corePoolSize时,如果是线程池会创建一个核心线程去处理提交的任务。
- 如果线程池核心线程数已满,即线程数已经大于等于corePoolSize,这时线程池会将任务放到一个任务队列workQueue排队等待执行。
- 当线程池里面存活的线程数已经大于等于corePoolSize了,并且任务队列workQueue也满,判断线程数是否达到maximumPoolSize,如果没到达,创建一个非核心线程执行提交的任务,否则执行线程池拒绝策略。
拒绝策略
- AbortPolicy(抛出一个异常,线程池默认的拒绝策略)
- DiscardPolicy(直接丢弃任务)
- DiscardOldestPolicy(丢弃队列里最老的任务,将当前这个任务继续提交给线程池)
- CallerRunsPolicy(交给线程池调用所在的线程进行处理)
JUC下的线程池
- newFixedThreadPool (固定数目线程的线程池)
- newCachedThreadPool(可缓存线程的线程池)
- newSingleThreadExecutor(单线程的线程池)
- newScheduledThreadPool(定时及周期执行的线程池)
固定长度线程池
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory);
}
特点
- 长度固定,核心线程数和最大线程数相等。
- 阻塞队列是无界阻塞队列LinkedBlockingQueue
工作机制
线程池运行过程可缓存线程的线程池
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(),
threadFactory);
}
特点
- 核心线程数为0
- 最大线程数为Integer.MAX_VALUE
- 线程闲置存活时间为60秒
工作机制
线程池运行过程单线程的线程池
public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory));
}
特点
- 长度固定,核心线程数和最大线程数相等,都为1。
- 阻塞队列是无界阻塞队列LinkedBlockingQueue
工作机制
线程池运行过程定时及周期执行的线程池
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
特点
- 最大线程数为Integer.MAX_VALUE
- 阻塞队列是DelayedWorkQueue
Java线程池配置原则
任务类型
-
CPU密集
CPU密集型的话,一般配置CPU处理器个数+/-1个线程,所谓CPU密集型就是指系统大部分时间是在做程序正常的计算任务,例如数字运算、赋值、分配内存、内存拷贝、循环、查找、排序等,这些处理都需要CPU来完成。 -
IO密集
IO密集型的话,是指系统大部分时间在跟I/O交互,而这个时间线程不会占用CPU来处理,即在这个时间范围内,可以由其他线程来使用CPU,因而可以多配置一些线程。 -
混合型
混合型的话,是指两者都占有一定的时间。
实例
public class ThreadPoolUtil {
/**
* 创建的线程设置有意义的名字,可方便排查问题。
*/
private static ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
.setNameFormat("demo-pool-%d").build();
/**
* 建议阻塞队列使用有界队列,既可以保证不会因为任务挤压导致系统崩溃,
* 同时也有助于我们及时的发现问题,作出响应(例如当阻塞队列满时预警)。
* 阻塞队列的最大值可根据实际业务场景自行设置
*/
private static ExecutorService executorService= new ThreadPoolExecutor(ioIntesivePoolSize(), ioIntesivePoolSize(),
0L, TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<>(100),namedThreadFactory,new ThreadPoolExecutor.AbortPolicy());
private ThreadPoolUtil() {
}
public static void exec(Runnable runnable){
executorService.execute(runnable);
}
public static void submit(Callable callable){
executorService.submit(callable);
}
/**
* Each tasks blocks 90% of the time, and works only 10% of its
* lifetime. That is, I/O intensive pool
* @return io密集的线程池
*/
public static int ioIntesivePoolSize() {
double blockingCoefficient = 0.9;
return poolSize(blockingCoefficient);
}
/**
* A computation-intensive task has a blocking coefficient of 0
* @return cpu intesive Thread pool size
*/
public static int cpuIntesivePoolSize() {
double blockingCoefficient = 1;
return poolSize(blockingCoefficient);
}
/**
*
* Number of threads = Number of Available Cores / (1 - Blocking
* Coefficient) where the blocking coefficient is between 0 and 1.
*
* A computation-intensive task has a blocking coefficient of 0, whereas an
* IO-intensive task has a value close to 1,
* so we don't have to worry about the value reaching 1.
* @param blockingCoefficient the coefficient
* @return Thread pool size
*/
public static int poolSize(double blockingCoefficient) {
int numberOfCores = Runtime.getRuntime().availableProcessors();
int poolSize = (int) (numberOfCores / (1 - blockingCoefficient));
return poolSize;
}
}
网友评论