线程池
1. 为什么使用线程池
Java虽然内置了多线程支持,启动一个新线程非常方便,但是,每次创建线程都要操作系统资源(线程资源,栈空间等),频繁创建和销毁大量线程需要消耗大量时间。
如果复用线程,将可以复用的线程分为一组:线程池,那么我们将任务分配给这一组线程,不用频繁的创建,销毁线程。
Snipaste_2023-03-13_20-27-54.png
线程池底层原理:
1. 底层使用集合: LinkedList<Thread> 执行任务的线程对象。 linkedList<Runnable> 目标执行的任务内容 ==> public void run() 方法 what will be run 2. 可以考虑使用【观察者模式】进行底层线程对象个数管理 用户执行任务等待时间超时 ==> 增加 Thread 实现类 Thread用户执行任务等待时间超时 ==> 增加 Thread 实现类 Thread 实现类无任务等待时间超时 ==> 关闭对应的 Thread 对象 3. 原来线程Thread对象,执行任务之后,自动关闭线程对象 需要将关闭操作转换为放回到线程池末尾,等待下一次任务 【动态代理模式】 反射技术 代理对象
2. 线程池的创建
Java 标准库提供了ExecutorService 接口表示线程池,它的经典用法:
ExecutorService 接口实现类:
- FixedThreadPool:线程数固定的线程池。
- CachedThreadPool:线程数根据任务动态调整的线程池。
- SingleThreadExecutor:仅单线程执行的线程池。
创建这些线程池的方法都在Executors类中(单例模式)。
newFixedThreadPool 固定线程
// 创建固定大小的线程池,线程数为3
ExecutorService executor = Executors.newFixedThreadPool(3);
// 提交任务到线程池中,task指的是任务,通常是Runnable接口中的run方法,Runnable 是函数式接口
executor.submit(task1);
// Runnable 接口:
/*
interface Runnable{
void run();
}
*/
// 栗子:使用Lambda表达式 提交任务
executor.submit(() -> System.out.println());
// 使用方法引用 提交任务
executor.submit(Task::task00);
executor.submit(new Task()::task01);
class Task {
public static void task00() {
// 方法体
}
public void task01() {
}
}
newCachedThreadPool 根据任务动态线程数 的线程池。
// newCachedThreadPool 源码:
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L,
TimeUnit.SECONDS,
new SynchronousQueue<Runnable> ());
}
ScheduledThreadPool:
此方法可以接收多个任务,需要提供的任务本身固定,定期反复执行。例如:每秒刷新证券价格。
ScheduledExecutorService ses = Executors.newScheduledThreadPool(4);
我们可以提交一次性任务,他会在指定延迟后只执行一次。
// 1秒后执行一次性任务: ses.schedule(new Task("one-time"), 1, TimeUnit.SECONDS);
如果任务以固定的每3秒执行:
// 2秒后开始执行任务,以三秒为间隔执行: ses.scheduleAtFixedRate(new Task("fixed-rate"), 2, 3, TimeUnit.SECONDS);
如果任务以固定三秒为间隔执行:
// 2秒后开始执行定时任务,以3秒为间隔执行: ses.scheduleWithFixedDelay(new Task("fixed-delay"), 2, 3, TimeUnit.SECONDS);
注意: FixedRate 和 FixedDelay 的区别。
- FixedRate:指的是任务以固定时间间隔触发,无论任务执行时间长短:
Snipaste_2023-03-13_20-28-29.png
- FixedDelay:指的是上次任务执行完成后,等待固定时间间隔,在执行下一个任务。
Snipaste_2023-03-13_20-28-48.png
Java标准库中提供了
java.util.Timer
类,这个类也可以定期执行任务,但是,一个Timer
会对应一个Thread
,所以一次只能定期执行一个任务。