目录
一、线程池的基本概念
二、线程池的工作原理
三、线程池的使用示例
四、线程池的拒绝策略
家人们,今天咱来唠唠 Java 多线程里的线程池,这可是个相当重要的知识点,在实际开发中用处特别大,能帮我们提升程序性能、管理资源。我也是在学习和实践中慢慢把它摸透了,现在就把我的经验分享给大伙。
一、线程池的基本概念
线程池,简单来说,就是一个存放线程的池子。在传统的多线程编程中,每处理一个任务就创建一个新线程,任务完成后线程销毁。但频繁地创建和销毁线程开销很大,线程池就解决了这个问题。它事先创建好一定数量的线程放在池子里,有任务来就从池子里拿线程去执行,任务完成后线程不销毁,而是回到池子里等待下一个任务。这就好比开了一家餐厅,有固定数量的服务员(线程),有客人(任务)来了就安排服务员去服务,服务完后服务员不离开,继续等待下一桌客人,这样能大大提高效率。
二、线程池的工作原理
Java 中的线程池主要通过ThreadPoolExecutor
类来实现,它的构造函数参数决定了线程池的核心属性:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
// 构造函数逻辑
}
- corePoolSize:核心线程数,线程池里会一直存活的线程数量。哪怕这些线程暂时空闲,也不会被销毁。就像餐厅里的固定员工数量,不管忙不忙,这些员工都在。
- maximumPoolSize:最大线程数,线程池能容纳的最大线程数量。当任务量突然增加,核心线程数不够用时,会创建新线程,直到达到最大线程数。
- keepAliveTime:线程存活时间,当线程数超过核心线程数时,多余的空闲线程能存活的最长时间。比如餐厅生意淡了,临时招来的服务员(超过核心线程数的线程)在空闲一段时间后就可以离开。
- unit:存活时间的单位,和
keepAliveTime
配合使用,如TimeUnit.SECONDS
表示秒。 - workQueue:任务队列,用来存放等待执行的任务。当所有线程都在忙时,新任务就会被放进这个队列里排队。
- threadFactory:线程工厂,用于创建新线程,通过它可以定制线程的一些属性,比如线程名。
- handler:拒绝策略,当任务队列满了,且线程数达到最大线程数,再有新任务来就会触发拒绝策略,决定如何处理这些无法被接受的任务。
三、线程池的使用示例
下面通过一个简单的示例来看看线程池的使用:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolExample {
public static void main(String[] args) {
// 创建一个固定大小为3的线程池
ExecutorService executorService = Executors.newFixedThreadPool(3);
for (int i = 0; i < 5; i++) {
int taskNumber = i;
executorService.submit(() -> {
System.out.println("线程 " + Thread.currentThread().getName() + " 开始执行任务 " + taskNumber);
try {
Thread.sleep(1000); // 模拟任务执行时间
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程 " + Thread.currentThread().getName() + " 完成任务 " + taskNumber);
});
}
// 关闭线程池
executorService.shutdown();
}
}
在这个示例中,我们使用Executors.newFixedThreadPool(3)
创建了一个固定大小为 3 的线程池。然后提交了 5 个任务,由于线程池大小为 3,前 3 个任务会立即被线程执行,剩下 2 个任务会进入任务队列等待。当有线程空闲时,就会从队列中取出任务继续执行。最后调用executorService.shutdown()
关闭线程池,它会不再接受新任务,并等待已提交的任务执行完毕。
四、线程池的拒绝策略
前面提到当线程池无法接受新任务时会触发拒绝策略,Java 提供了以下几种常见的拒绝策略:
- AbortPolicy:默认策略,直接抛出
RejectedExecutionException
异常,阻止系统正常运行。就像餐厅客满且排队也排不下时,直接拒绝新客人。 - CallerRunsPolicy:将任务回退给调用者,由调用者线程来执行这个任务。比如餐厅忙不过来时,让客人自己帮忙做点简单的服务工作。
- DiscardPolicy:直接丢弃新任务,不做任何处理。这就好比餐厅太忙了,直接无视新来的客人。
- DiscardOldestPolicy:丢弃任务队列中最老的任务(最先进入队列的任务),然后尝试提交新任务。就像餐厅把排队最久的客人赶走,给新客人腾位置。
家人们,线程池的学问可不少,要想在实际开发中用好它,还得不断实践和总结。要是你们在学习线程池的过程中有啥疑问,咱们随时交流,一起把这部分知识吃透!