一、什么是线程池
线程池是一个线程管理的工具类,它允许重用现有的线程,而不是每次新建一个线程就执行一个任务
二、工作原理
- 当任务提交到线程池后,线程池会判断当前运行的线程数量是否超过线程池的最大线程数量,如果没有超过,就直接使用运行的线程执行任务;
- 如果运行的线程数量达到最大数量,则新的任务会被放入阻塞队列中等待;
- 当运行的线程数量低于最大数量时,线程池会从阻塞队列中取出任务使用空闲线程执行。
三、解决的主要问题
- 降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
- 提高响应速度。当任务到来时,无需创建线程就可以立即执行任务。
- 提供线程限流保护。通过线程池控制线程最大数量,避免因无限制创建线程导致的内存溢出。
四、如何使用线程池
1.使用Executors提供的静态方法来创建常见的线程池
ExecutorService pool = Executors.newFixedThreadPool(5);
也可以自定义线程池
ThreadPoolExecutor pool = new ThreadPoolExecutor(
corePoolSize,
maximumPoolSize,
keepAliveTime,
TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>());
其中corePoolSize指的是核心池的大小
maximumPoolSize指最大线程数
keepAliveTime是超出核心池大小的线程的最大空闲时间
当线程池中的线程数大于核心线程池大小时,这时多出来的线程属于非核心线程。非核心线程如果处于空闲状态(没有被分配任务),线程池就会进行线程回收。keepAliveTime指的就是在非核心空闲线程能够存在的最大时间。
TimeUnit.seconds(days,hours,minutes) 这个就是keepAliveTime所存在时间的单位。
new LinkedBlockingQueue<Runnable>() 是为了缓冲任务,防止任务丢失。
2.提交任务
使用线程池执行任务使用execute()或submit()方法:
pool.execute(()->{
方法体
});
Future<?> future = pool.submit(()->{
方法体
});
3.关闭线程池
使用shutdown()方法关闭线程池,已提交的任务会继续执行,但不会接收新任务
pool.shutdown();
使用shutdownNow()方法可以取消正在执行的任务,并尝试停止所有线程
pool.shutdownNow();
使用awaitTermination()方法等待线程池的所有任务完成后再终止
pool.awaitTermination(60, TimeUnit.SECONDS);
五、线程池的生命周期
线程池的生命周期主要包括以下几个阶段:
1.初始化阶段:
当调用ThreadPoolExecutor的构造方法创建线程池对象时,就进入了初始化阶段。
这时会根据传入的参数,如线程池大小、线程工厂等来进行一些初始化设置。
2.运行阶段:
当向线程池中提交任务时,线程池进入运行阶段。这时线程池会根据当前任务数量和线程池设置,动态的添加或移除线程。
具体来说:如果当前线程数小于corePoolSize,直接创建新线程运行任务,当前线程数等于corePoolSize,则任务进入队列等待,队列已满且当前线程数小于maximumPoolSize,则创建新线程运行任务,当前线程数等于maximumPoolSize,则使用拒绝策略处理新任务
3.销毁阶段:
当不再向线程池提交新任务,并且所有任务都执行完毕时,线程池进入销毁阶段。
这时线程池会根据设置进行优雅关闭
等待所有任务完成
终止当前线程并清理资源
线程池对象资源被回收