
1、线程池是什么 虽然创建线程/销毁线程的开销
想象这么⼀个场景:
在学校附近新开了一家快递店,⽼板很精明,想到一个与众不同的办法来经营。店里没有雇人,而是每次有业务来了,就现场找一名同学过来把快递送了,然后解雇同学。这个类比我们平时来一个任务,起一个线程进行处理的模式。
很快老板发现问题来了,每次招聘+解雇同学的成本还是非常高的。老板还是很善于变通的,知道了为什么大家都要雇人了,所以指定了一个指标,公司业务人员会扩张到3个人,但还是随着业务逐步雇人。于是再有业务来了,老板就看,如果现在公司还没3个人,就雇一个人去送快递,否则只是把业务放到一个本本上,等着3个快递人员空闲的时候去处理。这个就是我们要带出的线程池的模式。
线程池最大的好处就是减少每次启动、销毁线程的损耗。
线程池和协程(纤程)的技术
核心是解释 “为何需要线程池和协程”:

这里为什么说Go语言能够威胁到Java的地位,就是他更擅长处理并发编程
1.代码更加简单(CSP编程模型)
2.使用协程,相比于线程性能更高
返回值类型为ExecutorService。
通过ExecutorService.submit可以注册一个任务到线程池中。
ExecutorService pool = Executors.newFixedThreadPool(10);
pool.submit(new Runnable() {
@Override
public void run() {
System.out.println("hello");
}
});Executors 创建线程池的几种方式:
newFixedThreadPool:创建固定线程数的线程池
newCachedThreadPool:创建线程数目动态增长的线程池
newSingleThreadExecutor: 创建只包含单个线程的线程池
newScheduledThreadPool:设定延迟时间后执行命令,或者定期执行命令,是进阶版的Timer。
Executors 本质上是ThreadPoolExecutor类的封装。
ThreadPoolExecutor 提供了更多的可选参数,可以进⼀步细化线程池行为的设定

corePoolSize:正式员工的数量。(正式员工,一旦录用,永不辞退)
maximumPoolSize:正式员工+临时工的数目。(临时工:一段时间不干活,就被辞退)
keepAliveTime:临时工允许的空闲时间
unit:keepaliveTime 的时间单位,是秒,分钟,还是其他值
workQueue:传递任务的阻塞队列
threadFactory:创建线程的工厂,参与具体的创建线程工作,通过不同线程工厂创建出的线程相当于对一些属性进行了不同的初始化设置
RejectedExecutionHandler:拒绝策略,如果任务量超出公司的负荷了接下来怎么处理
AbortPolicy(): 超过负荷,直接抛出异常
CallerRunsPolicy():调用者负责处理多出来的任务
DiscardOldestPolicy():丢弃队列中最⽼的任务
DiscardPolicy():丢弃新来的任务
使用Worker类描述一个工作线程,使用Runnable描述一个任务
使用一个BlockingQueue组织所有的任务
每个worker线程要做的事情:不停的从BlockingQueue中取任务并执行
指定一下线程池中的最大线程数maxWorkerCount:当当前线程数超过这个最大值时,就不再新增线程了
class MyThreadPool {
private BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();
// 通过这个⽅法, 来把任务添加到线程池中.
public void submit(Runnable runnable) throws InterruptedException {
queue.put(runnable);
}
// n 表⽰线程池⾥有⼏个线程.
// 创建了⼀个固定数量的线程池.
public MyThreadPool(int n) {
for (int i = 0; i < n; i++) {
Thread t = new Thread(() -> {
while (true) {
try {
// 取出任务, 并执⾏~~
Runnable runnable = queue.take();
runnable.run();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
}
}
} // 线程池
public class Demo {
public static void main(String[] args) throws InterruptedException {
MyThreadPool pool = new MyThreadPool(4);
for (int i = 0; i < 1000; i++) {
pool.submit(new Runnable() {
@Override
public void run() {
// 要执⾏的⼯作
System.out.println(Thread.currentThread().getName() + " hello");
}
});
}
}
}
RejectedExectionHandler handler 拒绝策略
整个线程七个参数中,最重要,最复杂的
面试官考察,线程池的参数含义,最想听的就是你对第七个参数的理解
submit 把创建的线程提交到任务队列当中去,任务队列的实现是用阻塞队列
队列满了,在添加,就会阻塞~~
但是我们不希望再这里阻塞太多
对于线程池来说,发现入队列操作时,队列满了,不会真的触发“入队列操作”,不会真真阻塞,而且执行拒绝策略相关的代码

也就是这四个策略

所以,Java 标准库,也提供了另一组类,针对 ThreadPoolExecutor 进行了进一步封装,简化线程池的使用.
Executors.newFixedThreadPool(4);核心线程数和最大线程数一样~~Executors.newCachedThreadPool();最大线程数是一个很大的数字.(线程可以无限增加)createCircle()、createSquare()),用相同参数创建不同实例,解决了构造方法重载的限制。
public MyThreadPool(int n) {
// 初始化线程池,创建固定个数的线程
// 这里使用ArrayBlockingQueue作为任务队列,容量为1000
queue = new ArrayBlockingQueue<>(1000);
// 创建 N 个线程
for (int i = 0; i < n; i++) {
Thread t = new Thread(() -> {
try {
while (true) {
Runnable task = queue.take();
task.run();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
}始化一个线程池的任务队列:
ArrayBlockingQueue 是 Java 中的一个有界阻塞队列,它基于数组实现,具有固定的容量(这里设置为 1000)。package thread;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
// 实现一个固定线程个数的线程池
class MyThreadPool {
private BlockingQueue<Runnable> queue = null;
public MyThreadPool(int n) {
// 初始化线程池,创建固定个数的线程
// 这里使用ArrayBlockingQueue作为任务队列, 容量为1000
queue = new ArrayBlockingQueue<>(1000);
// 创建 N 个线程
for (int i = 0; i < n; i++) {
Thread t = new Thread(() -> {
try {
while (true) {
Runnable task = queue.take();
task.run();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
// t.setDaemon(true);
t.start();
}
}
public void submit(Runnable task) throws InterruptedException {
// 将任务放入队列中
queue.put(task);
}
}
public class Demo35 {
public static void main(String[] args) throws InterruptedException {
MyThreadPool pool = new MyThreadPool(10);
// 向线程池提交任务
for (int i = 0; i < 100; i++) {
int id = i;
// 临时变量,每次循环都是新的,且不会被修改
pool.submit(() -> {
System.out.println(Thread.currentThread().getName() + " id=" + id);
});
}
}
}lambda 表达式中引用的局部变量必须是最终的(final)或事实上最终的(effectively final)。
所以我们选择用一个临时变量id 来保证这个是不会修改的
shutdown() 和 awaitTermination() 的区别
1、使用没有共享资源的模型
2、适用共享资源只读,不写的模型
a. 不需要写共享资源的模型
b. 使用不可变对象
3、直面线程安全(重点)
a. 保证原子性
b. 保证顺序性
c. 保证可见性
Java 提供了多种 BlockingQueue 实现,不同队列适用于不同场景,选择时需考虑是否有界(是否限制长度)、排序方式等特性:

LinkedBlockingQueue(默认无界,推荐)new LinkedBlockingQueue<>(100)),避免任务无限积压。// 有界队列:最多存放 100 个任务
BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>(100);
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2, 4, 60L, TimeUnit.SECONDS,
queue // 使用有界 LinkedBlockingQueue
);ArrayBlockingQueue(有界,需指定容量)// 容量为 50 的数组队列
BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(50);
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2, 4, 60L, TimeUnit.SECONDS,
queue
);SynchronousQueue(无缓冲,任务直接传递)Executors.newCachedThreadPool() 底层使用)。BlockingQueue<Runnable> queue = new SynchronousQueue<>();
ThreadPoolExecutor executor = new ThreadPoolExecutor(
0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS,
queue // 任务需立即被线程处理,无缓冲
);LinkedBlockingQueue 默认无界,若任务提交速度远快于处理速度,队列会无限增长,最终导致内存溢出(OOM)。建议指定队列容量(如 new LinkedBlockingQueue<>(1000))。
AbortPolicy、CallerRunsPolicy)处理超额任务,避免任务丢失或系统崩溃。
public class ThreadPoolQueueDemo {
public static void main(String[] args) {
// 1. 创建有界任务队列(容量 3)
BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>(3);
// 2. 创建线程池(核心 2,最大 4,队列 3)
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2, 4, 60L, TimeUnit.SECONDS,
queue,
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy() // 队列满时抛出异常
);
// 3. 提交 8 个任务(观察队列和线程的协作)
for (int i = 0; i < 8; i++) {
int taskId = i;
executor.submit(() -> {
try {
Thread.sleep(1000); // 模拟任务执行
System.out.println("任务 " + taskId + " 执行完毕,线程:" + Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
});
System.out.println("提交任务 " + taskId + ",当前队列大小:" + queue.size());
}
executor.shutdown();
}
}ThreadPoolExecutor)线程池的核心实现是 ThreadPoolExecutor,通过 7 个参数可灵活配置,避免 Executors 工具类可能带来的资源风险(如无界队列导致 OOM)。
new ThreadPoolExecutor(
int corePoolSize, // 核心线程数(常驻线程,默认不回收)
int maximumPoolSize, // 最大线程数(核心 + 非核心线程的上限)
long keepAliveTime, // 非核心线程空闲超时时间(超时后回收)
TimeUnit unit, // 超时时间单位(如 SECONDS、MILLISECONDS)
BlockingQueue<Runnable> workQueue, // 任务队列(缓冲待执行的任务)
ThreadFactory threadFactory, // 线程工厂(定义线程的创建方式,如名称、优先级)
RejectedExecutionHandler handler // 拒绝策略(任务满时的处理方式)
)线程池提供两种提交任务的方法,根据是否需要返回值选择:
execute(Runnable)适用于只需要执行任务,无需获取结果的场景。
// 提交 Runnable 任务(无返回值)
executor.execute(() -> {
System.out.println("执行无返回值任务,线程:" + Thread.currentThread().getName());
// 任务逻辑...
});submit(Callable) 或 submit(Runnable)submit(Callable<V>):返回 Future<V>,可通过 get() 获取任务结果。submit(Runnable):返回 Future<?>,get() 结果为 null(仅用于判断任务是否完成)// 提交 Callable 任务(有返回值)
Future<Integer> future = executor.submit(() -> {
int result = 0;
for (int i = 1; i <= 100; i++) {
result += i;
}
return result; // 返回计算结果
});
// 获取结果(会阻塞直到任务完成)
try {
int sum = future.get(); // 获取返回值
System.out.println("任务结果:" + sum);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace(); // 捕获任务执行中的异常
}线程池的核心线程默认不会自动销毁,若不关闭,程序会一直运行。需调用以下方法:

// 平缓关闭(推荐)
executor.shutdown();
// 可选:等待线程池关闭(最多等 5 秒)
try {
if (!executor.awaitTermination(5, TimeUnit.SECONDS)) {
// 若超时,强制关闭剩余任务
executor.shutdownNow();
}
} catch (InterruptedException e) {
executor.shutdownNow();
}import java.util.concurrent.*;
public class ThreadPoolUsage {
public static void main(String[] args) {
// 1. 配置任务队列(有界队列,容量 5,避免 OOM)
BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(5);
// 2. 自定义线程工厂(设置线程名称,便于调试)
ThreadFactory threadFactory = new ThreadFactory() {
private int threadId = 1;
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r);
thread.setName("biz-thread-" + threadId++); // 线程名称前缀
thread.setDaemon(false); // 非守护线程(默认)
return thread;
}
};
// 3. 定义拒绝策略(队列满时,让提交任务的线程自己执行,缓解压力)
RejectedExecutionHandler handler = new ThreadPoolExecutor.CallerRunsPolicy();
// 4. 创建线程池:核心 2 个,最大 4 个,非核心线程空闲 30 秒回收
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2, 4, 30, TimeUnit.SECONDS,
workQueue, threadFactory, handler
);
// 5. 提交 8 个任务(测试线程池的调度逻辑)
for (int i = 1; i <= 8; i++) {
int taskId = i;
// 提交无返回值任务
executor.execute(() -> {
try {
System.out.println("任务 " + taskId + " 开始,线程:" + Thread.currentThread().getName());
Thread.sleep(1000); // 模拟任务耗时
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
// 6. 关闭线程池
executor.shutdown();
}
}