线程池
1、 线程池概念
如果有非常多的任务需要非常多的线程来完成,每个线程的工作时间不长,就需要创建很多线程,工作完又立即销毁[
线程频繁创建和销毁线程
]频繁创建和销毁线程非常消耗性能,那么线程池,就是可以创建一些线程,放在"池子"中,用的时候去池子取一个线程去使用,使用完再放回去,线程可以重用
线程池,底层其实就是集合队列,里面存储线程对象,用的时候去抽即可,就不要频繁创建线程了
使用线程池的好处是
减少在创建和销毁线程上所花的时间以及系统资源的开销,解决资源不足的问题。
如果不使用线程池,有可能造成系统创建大量同类线程而导致消耗完内存(
OOM
Out Of Memory)或者“过度切换”的问题--> 以上摘自阿里官方手册
2、 线程池原理
将任务(task)提交(submit/execute)给线程池(threadpool),由线程池分配线程,运行任务,任务结束后,线程重新放入线程池供后续线程使用
3、 创建线程池的方式
使用线程池创建线程,执行任务
JDK提供了关于创建线程池的方式
Executors
: 通过该类提供的静态方法
来获得不同特点的线程池对象
newFixedThreadPool
newCachedThreadPool
newScheduledThreadPool
newSingleThreadExecutor
...
ThreadPoolExecutor
: 通过submit(Runnable task) 来提交任务,执行任务
线程池执行任务时,可以采用两种方法:
execute(): 没有返回值,无法判断任务是否执行成功
submit():会返回Future对象,通过该对象判断任务是否执行成功
线程池使用完要关闭时:
shutdown() 关闭线程池
4、 不同特点的线程池
通过Executors调用以下静态方法获得不同特点的线程池对象
方法 类型 解释 newFixedThreadPool 固定大小
线程池池中包含固定数目的线程,空闲线程一直保留。只有核心线程,线程数量固定,任务队列为LinkedBlockingQueue newCachedThreadPool 动态大小
的线程池,原则上无上限无核心线程,非核心线程数量无限,执行完闲置60s后回收,任务队列SynchronousQueue newScheduledThreadPool 可以执行 定时任务
的线程池用于调度执行的固定线程池,执行定时或周期性任务。和弦线程数量固定,非核心线程数量无线,执行完闲置10ms后回收,任务队列为DelayedWorkQueue newSingleThreadExecutor 单
线程线程池只有一个线程的池,会顺序执行提交的任务,只有一个核心线程,无非核心线程,任务队列为LinkdBlockingQueue newSingleThread ScheduledExecutor 单线程定时任务
线程池newWorkStealingPool 1.8提供新的方式创建线程池
以上线程池操作在阿里java开发手册中是不建议用的.....
说明:Executors 返回的线程池对象的弊端如下: 1)FixedThreadPool 和 SingleThreadPool: 允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。 2)CachedThreadPool 和 ScheduledThreadPool: 允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。 ----------------------- OOM 内存溢出,即系统资源耗尽
分别演示不同特点的线程池:
package com.qf.theadpool; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; /** * --- 天道酬勤 --- * * @author QiuShiju * @date 2024/3/15 * @desc */ public class TestThreadPool { public static void main(String[] args) { } private static void show3() { // 创建一个调度功能的线程池 ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(3); // 给线程池提交任务 for (int i = 1; i < 11; i++) { threadPool.schedule(new Runnable( ) { @Override public void run() { Thread thread = Thread.currentThread( ); System.out.println(thread.getName( ) + "执行任务"); } },5, TimeUnit.SECONDS); } threadPool.shutdown( ); } private static void show2() { // 缓存线程池(可变大小) ExecutorService threadPool = Executors.newCachedThreadPool( ); // 给线程池提交任务 for (int i = 1; i < 10001; i++) { threadPool.execute(new Runnable( ) { @Override public void run() { Thread thread = Thread.currentThread( ); System.out.println(thread.getName( ) + "执行任务"); } }); } threadPool.shutdown( ); } private static void show1() { // 创建一个固定3个的线程池 ExecutorService threadPool = Executors.newFixedThreadPool(3); // 给线程池提交10个任务 for (int i = 1; i < 11; i++) { threadPool.execute(new Runnable( ) { @Override public void run() { Thread thread = Thread.currentThread( ); System.out.println(thread.getName( ) + "执行任务"); } }); } // 关闭线程池 threadPool.shutdown( ); } }
5、 ThreadPoolExecutor[重要]
ThreadPoolExecutor
很重要,有7个参数
参数名 解释 备注 int corePoolSize 线程池的线程数量(核心线程数) 不能小于0 int maximumPoolSize 线程池可支持的最大线程数 最大数量>=核心线程数 long keepAliveTime 指定临时线程的最大存活时间 不能小于0 TimeUnit unit 指定存活时间的单位(秒,分,时,天) 时间单位 BlockingQueue<Runnable> workQueue 指定任务队列 ThreadFactory threadFactory 指定哪个线程工厂创建线程 RejectedExecutionHandler handler 指定线程忙,任务队列满的时候新任务来了怎么办?拒绝策略
举例子: 海底捞吃饭
核心线程数: 核心服务人员3个
最大线程数: 允许最多的服务人员数量10, (其中7个临时找的)
最大存活时间: 临时工不干活时间
时间单位:
阻塞队列: 门口的排队的人
线程工厂: 如何将服务人员(线程)创建来的
拒绝策略: 再来的任务不再接收直接拒绝(发券下次来,本次不接客...)
public static void main(String[] args) {
ArrayBlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(10);
ThreadPoolExecutor pool = new ThreadPoolExecutor(
3, // 核心线程数
10,// 最大线程数
10, // 最大存活时间
TimeUnit.SECONDS,// 时间单位
queue);// 阻塞队列
// 给线程池提交任务
for (int i = 1; i < 30; i++) {
pool.execute(new Runnable( ) {
@Override
public void run() {
Thread thread = Thread.currentThread( );
System.out.println(thread.getName( ) + "执行任务");
}
});
}
}