ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Java线程池总结

Java线程池总结

1.了解线程池

如果每次来一个任务,就新建一个线程、任务做完销毁线程,频繁创建和销毁线程开销很大,非常浪费资源。

线程池就相当于提前准备好一批线程放在池子里面,任务来了直接交给池子里已有的线程去执行.任务多的时候,任务先放到阻塞队列排队. 线程空闲了,就从队列里取出下一个任务继续干活。

线程池最大好处:减少频繁创建、销毁线程带来的性能损耗。

我们用快递店举个例子:

  • 不使用线程池:来一单快递,临时找一个同学送,送完直接开除。每次招人、辞退成本很高。
  • 使用线程池:提前固定雇好 3 个快递员(池子里的线程)。单子少的时候,快递员干完活待命. 单子多了,单子写在本子上(阻塞任务队列),等快递员空闲再处理。

2. 标准库中的线程池

  • 使用Executors.newFixedThreadPool(10) 能创建出固定包含10个线程的线程池
  • 返回值类型为 ExecutorService
  • 通过ExecutorService.submit 可以注册一个任务到线程池中
public class ThreadPool { public static void main(String[] args) { 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 类的封装.

3.ThreadPoolExecutor 核心参数介绍 (重点!!!)

ThreadPoolExecutors提供了更多的可选参数,可以进一步细化线程池行为的设定

逐个通俗解释:

  1. corePoolSize:核心线程数(正式员工)核心线程一旦创建,正常情况下不会被回收,就算没事干也留在池子里待命。
  2. maximumPoolSize:最大线程数核心线程 + 临时线程 的总数上限。
  3. keepAliveTime:空闲存活时间临时线程空闲多久之后,就会被销毁辞退。
  4. unit:时间单位配合上面的 keepAliveTime 使用,可以指定是秒、分钟、小时等。
  5. workQueue:任务阻塞队列存放等待执行任务的队列,就是我们前面手写的阻塞队列。当核心线程全部忙完,新来的任务就先放进这个队列排队。
  6. threadFactory:线程工厂用来创建线程,可以自定义线程名字、优先级等属性。一般直接用默认的工厂即可。
  7. handler:拒绝策略当队列满了,并且线程数量已经达到最大线程数,再来新任务,就执行拒绝策略。一共 4 种内置策略:
  • AbortPolicy:直接抛出异常(默认策略)
  • CallerRunsPolicy:交给提交这个任务的线程自己去执行
  • DiscardOldestPolicy:丢掉队列里等待最久的老任务,再尝试加入新任务
  • DiscardPolicy:直接丢弃新来的任务,不报错

4.线程池工作流程

任务提交到了新线程之后, 开始判断顺序 :

  1. 来了一个新任务,先判断当前正在工作的核心线程数量有没有小于corePoolSize
    • ✅ 小于:直接新建一个核心线程,执行这个任务
    • ❌ 不小于:进入下一步
  2. 判断阻塞任务队列 workQueue 是否已满
    • ✅ 队列没满:把任务放进阻塞队列排队等待
    • ❌ 队列已经满了:进入下一步
  3. 判断当前总线程数有没有小于 maximumPoolSize
    • ✅ 小于:新建临时线程,处理这个任务;临时线程空闲超过 keepAliveTime 就会被回收
    • ❌ 已经达到最大线程数:进入下一步
  4. 触发拒绝策略,按照设定的 handler 处理这个新任务

一句话简化:先核心线程 → 队列排队 → 临时线程 → 拒绝策略

5.实现线程代码

线程池的本质 = 一批常驻的Worker 线程 + 阻塞任务队列

Worker 线程不停循环, 从阻塞队列 take() 取任务执行

外部调用 submit 把任务 put 进队列

class MyThreadPool{ private BlockingDeque<Runnable>queue=new LinkedBlockingDeque<>(); //通过这个方法,来把任务添加到线程池中 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){ Runnable runnable= null; try { //取出任务并执行 runnable = queue.take(); runnable.run(); } catch (InterruptedException e) { throw new RuntimeException(e); } } }); t.start(); } } } //线程池 public class ThreadPool { 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"); } }); } } }

运行结果:(部分)

6.总结

  1. 线程池核心目的:避免频繁创建销毁线程,节约系统开销, 底层依赖阻塞队列。
  2. 7 大参数:核心线程数、最大线程数、空闲时间、时间单位、阻塞队列、线程工厂、拒绝策略。
  3. 执行顺序:核心线程 → 队列 → 临时线程 → 拒绝策略。
  4. Executors 提供 4 种快捷创建方式:Fixed、Cached、Single、Scheduled,但是生产慎用。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进