Ciallo~(∠・ω< )⌒☆

Java 8 JUC 知识点汇总

去年 11 月的时候莫名其妙接到了申通快递的面试,想起来我 4 个月前投递过,看来是捞起来被 KPI 面了。 已经是 2026 年了,Java 25 都作为 LTS 了,虚拟线程都业务开发中的锁设计和同异步性能差异干碎了。 还在问 AQS,还在问 AQS 三剑客,最后由于不熟悉 CountDownLatch 的写法而被评价为不匹配,心累。

毁灭吧,Java。

JUC 主要知识点如下:

ThreadPoolExecutor

ThreadPoolExecutor + CompletableFuture 是 Java 8 并发编程的基石

线程池八大参数

线程池的调度顺序

  1. 查看核心线程是否可用
  2. 如不可用,则加入队列
  3. 如队列满,则检查最大线程容量,尝试创建非核心线程
  4. 如无法创建非核心线程,则执行拒绝策略

我个人常年使用线程池一直处于误区:

我认为核心线程满的时候会直接创建非核心线程,无法创建非核心线程才会加入工作队列,这是错误的。

仔细揣摩这样的策略,会发现这使得 CorePoolSize 这个参数的设计比较尴尬。

线程池的设计初衷,是池化设计 + 预加载的结合,用运行时廉价的队列缓冲代替昂贵的线程创建

常用的阻塞队列

ArrayBlockingQueue 数组阻塞队列

LinkedBlockingQueue 链表阻塞队列

SynchronousQueue 同步移交队列

PriorityBlockingQueue 优先阻塞队列

DeleyedWorkQueue 延迟队列

为什么使用阻塞队列而不是普通队列?

阻塞队列特地放在 JUC 包下,说明他是线程安全的。

阻塞二字,指的是阻塞队列特有的操作可能会阻塞该线程。(阻塞行为实现了同步,同步即串行,也就是线程安全)

普通队列

1Queue<String> normalQueue = new LinkedList<>();
2// 基本操作
3normalQueue.offer("task");  // 入队,失败返回false
4normalQueue.poll();         // 出队,队列空返回null
5normalQueue.peek();         // 查看,队列空返回null

阻塞队列

1BlockingQueue<String> blockingQueue = new LinkedBlockingQueue<>();
2// 阻塞操作
3blockingQueue.put("task");  // 入队,队列满时阻塞等待
4blockingQueue.take();       // 出队,队列空时阻塞等待

阻塞队列的核心设计

等待 / 通知机制

 1// 阻塞队列的简化实现
 2public class SimpleBlockingQueue<E> {
 3    private final Queue<E> queue = new LinkedList<>();
 4    private final int capacity;
 5    
 6    public SimpleBlockingQueue(int capacity) {
 7        this.capacity = capacity;
 8    }
 9    
10    // 阻塞式入队
11    public synchronized void put(E item) throws InterruptedException {
12        while (queue.size() == capacity) {  // 队列满
13            wait();  // 释放锁,等待
14        }
15        queue.offer(item);
16        notifyAll();  // 通知消费者
17    }
18    
19    // 阻塞式出队
20    public synchronized E take() throws InterruptedException {
21        while (queue.isEmpty()) {  // 队列空
22            wait();  // 释放锁,等待
23        }
24        E item = queue.poll();
25        notifyAll();  // 通知生产者
26        return item;
27    }
28}

Java 8 实际底层设计:可重入锁 + Condition