JUC相关速记
JUC相关速记
集合

| 接口 | 一句话总结 |
|---|---|
| Set | 无序、不可重复的集合,类似数学上的集合概念。 |
| List | 有序、可重复的集合,支持按索引访问,类似动态数组。 |
| Map | 键值对(Key-Value)映射表,Key 不可重复。 |
| Queue | 先进先出(FIFO)的队列,用于任务排队。 |
| Deque | 双端队列,支持在头尾两端插入/删除,既可作队列也可作栈。 |
Collection
-
Set
- 线程不安全
- HashSet:基于 HashMap 实现,无序,O(1) 增删查,允许 null。
- LinkedHashSet:HashSet + 双向链表,维护插入顺序,迭代有序。
- TreeSet:基于 TreeMap(红黑树)实现,自动排序(自然或比较器),O(log n)。
- 线程安全
- Collections.synchronizedSet:新创建了一个包装类,所有方法都用
synchronized(mutex)包裹保障线程安全 - CopyOnWriteArraySet:基于 CopyOnWriteArrayList实现,写时复制(Copy-On-Write)),适合读多写极少的场景。
- 就是 CopyOnWriteArrayList 的包装类,底层直接持有一个
CopyOnWriteArrayList对象。 - 所有操作(add/remove/contains)都委托给底层的 ArrayList 方法。
- 就是 CopyOnWriteArrayList 的包装类,底层直接持有一个
- ConcurrentSkipListSet: 线程安全的 TreeSet(有序 Set),基于ConcurrentSkipListMap实现
- Collections.synchronizedSet:新创建了一个包装类,所有方法都用
- 线程不安全
-
List
-
线程不安全
- ArrayList:基于动态数组,随机访问快(O(1)),中间插入/删除慢(O(n))。
- LinkedList:基于双向链表,插入/删除快(O(1)),随机访问慢(O(n)),可作 List/Queue/Deque。
-
线程安全
-
Vector:老式 ArrayList,方法全用
synchronized修饰(线程安全但性能差),已过时。 -
Collections.synchronizedList:新创建了一个包装类,所有方法都用
synchronized(mutex)包裹保障线程安全 -
CopyOnWriteArrayList:写时复制(Copy-On-Write),适合读多写极少的场景。
- 所有修改操作(add/remove/set):加锁(ReentrantLock),复制一份底层数组的全新副本,在副本上修改,然后让 volatile 数组引用指向新副本。
- 所有读操作(get/iterator):无锁,直接读当前数组引用(volatile 保证可见性)。
-
-
-
Map
-
线程不安全
-
HashMap:最常用 Map,基于哈希表,O(1) 增删查,允许 null key/value,无序。
-
LinkedHashMap:HashMap + 双向链表,维护插入顺序或访问顺序(可用于 LRU 缓存)。
-
TreeMap:基于红黑树,Key 自动排序,O(log n),不允许 null key(但允许 null value)。
-
-
线程安全
- HashTable:老式 HashMap,全方法
synchronized,不允许 null key/value,已过时。 - Collections.synchronizedMap:新创建了一个包装类,所有方法都用
synchronized(mutex)包裹保障线程安全 - ConcurrentHashMap:高并发哈希表,桶锁 + CAS(桶位为空(没有元素)时CAS 无锁插入,该桶位已有元素(发生哈希冲突)****),比 HashTable 性能好得多。
- 分段锁/CAS:桶位为空(没有元素)时CAS 无锁插入,该桶位已有元素(发生哈希冲突)时synchronized 锁住该桶位的头节点
- ConcurrentSkipListMap: 线程安全的 TreeMap(有序 Map),跳表(CAS无锁)
- 基于跳表实现,O(log n) 时间复杂度,支持 Key 有序遍历和范围查询。
- 如果需要排序且并发高,用 ConcurrentSkipListMap。
- HashTable:老式 HashMap,全方法
-
-
Queue
- 线程不安全
- PriorityQueue:基于二叉堆(优先级堆),按优先级出队(自然或比较器),非 FIFO。
- 线程安全
- ArrayBlockingQueue、LinkedBlockingQueue、PriorityBlockingQueue(阻塞队列BlockingQueue 系列):线程安全、阻塞、队列
- ConcurrentLinkedQueue:线程安全、非阻塞、单端(FIFO),底层是链表 + CAS(无锁)。
- 线程不安全
-
Deque:
Deque是Queue的子接口,Deque比Queue多了双端操作能力。-
线程不安全
-
LinkedList:双向链表
-
ArrayDeque:循环数组。取数据,head移动1,放东西,tail移动1
ArrayDeque是动态扩容的,当head == tail时(实际是容量不够时)会翻倍扩容,且不允许存null。
-
-
线程安全
- LinkedBlockingDeque:线程安全、阻塞、双端,链表 + ReentrantLock
- ConcurrentLinkedDeque:线程安全、非阻塞、双端,链表 + CAS(无锁)
-
选型核心:是List/Set/Map还是Queue/Deque、是否线程安全、是否阻塞、单端还是双端。
一、是 List/Set/Map
│
├─ 需要线程安全吗?
│ ├─ 否(单线程)→
│ │ ├─ List → ArrayList(首选,随机访问快)/ LinkedList(频繁头尾增删)
│ │ ├─ Set → HashSet(无序)/ LinkedHashSet(插入有序)/ TreeSet(自动排序)
│ │ └─ Map → HashMap(首选)/ LinkedHashMap(插入/访问有序)/ TreeMap(Key 排序)
│ │
│ └─ 是(多线程)→
│ ├─ 读多写极少 → CopyOnWriteArrayList / CopyOnWriteArraySet
│ ├─ 高并发 Map → ConcurrentHashMap
│ ├─ 高并发 Set(JDK 8+)→ ConcurrentHashMap.newKeySet()
│ └─ 通用包装 → Collections.synchronizedXXX(不推荐,全方法加锁,性能差)
│
└─ 额外考虑:顺序性(插入/访问/自然排序)、是否允许 null、初始容量
二、是 Queue/Deque
│
├─ 需要线程安全?
│ ├─ 否(单线程)→
│ │ ├─ 单端 → ArrayDeque(首选,性能最优)/ PriorityQueue(需优先级)
│ │ └─ 双端 → ArrayDeque(首选)/ LinkedList(需同时作 List 时)
│ │
│ └─ 是(多线程)→
│ ├─ 需要阻塞?
│ │ ├─ 否(非阻塞)→
│ │ │ ├─ 单端 → ConcurrentLinkedQueue(CAS 无锁)
│ │ │ └─ 双端 → ConcurrentLinkedDeque(CAS 无锁)
│ │ │
│ │ └─ 是(阻塞)→
│ │ ├─ 单端 →
│ │ │ ├─ 需要优先级 → PriorityBlockingQueue(无界,注意 OOM)
│ │ │ └─ 普通 FIFO → LinkedBlockingQueue(首选,两把锁吞吐高)
│ │ │ ArrayBlockingQueue(固定大小,内存紧凑)
│ │ └─ 双端 → LinkedBlockingDeque(一把锁,工作窃取场景)
│ │
│ └─ 特殊:SynchronousQueue(手递手,线程池 CachedThreadPool 用)
│ DelayQueue(延迟队列,元素到期才能取)
│
└─ 额外考虑:是否有界(防止 OOM)、锁机制(单锁/双锁/CAS)
核心决策原则(记住这 3 条)
- 默认选
ArrayList+HashMap+HashSet,遇到性能瓶颈再优化。 - 多线程首选
ConcurrentHashMap+BlockingQueue,避开synchronizedXXX包装类。 LinkedList基本可以忘记,ArrayDeque在栈/队列场景下全面替代它。
Concurrent 集合类
Concurrent 类是 CAS + 锁混合使用的:
-
Concurrent 开头的实现分三类:
- 纯 CAS 无锁:
ConcurrentLinkedQueue、ConcurrentSkipListMap,性能最高; - CAS + 锁混合:
ConcurrentHashMap(JDK 8+),桶空用 CAS,桶冲突用synchronized锁头节点; - 纯锁:
CopyOnWriteArrayList(写锁)、BlockingQueue系列(ReentrantLock),因为阻塞语义必须用锁。
- 纯 CAS 无锁:
-
类 主要手段 是否用锁 锁粒度 ConcurrentHashMapCAS + synchronized ✅ 用锁 桶级别(仅锁头节点) ConcurrentLinkedQueue纯 CAS ❌ 无锁 无锁(无阻塞算法) ConcurrentLinkedDeque纯 CAS ❌ 无锁 无锁 ConcurrentSkipListMap纯 CAS ❌ 无锁 无锁(跳表指针 CAS) ConcurrentSkipListSet纯 CAS ❌ 无锁 内部持有上面的 Map CopyOnWriteArrayListReentrantLock ✅ 用锁 写操作全局锁 CopyOnWriteArraySetReentrantLock ✅ 用锁 内部持有上面的 List LinkedBlockingQueueReentrantLock ✅ 用锁 两把锁(takeLock + putLock) ArrayBlockingQueueReentrantLock ✅ 用锁 一把锁(所有操作共用) PriorityBlockingQueueReentrantLock ✅ 用锁 一把锁 LinkedBlockingDequeReentrantLock ✅ 用锁 一把锁
面试追问:为什么
ConcurrentHashMap不用纯 CAS?"因为纯 CAS 只适合'无冲突'或'轻量冲突'的场景。当哈希冲突严重时,CAS 自旋重试会占用大量 CPU,反而降低性能。JDK 8 改成'桶空 CAS + 桶冲突 synchronized',既利用了 CAS 的快速路径,又用锁避免了高冲突下的 CPU 空转。"
阻塞队列API
| 操作 | 抛异常 | 返回特殊值 | 阻塞 | 超时退出 |
|---|---|---|---|---|
| 插入 | add(e) |
offer(e) |
put(e) |
offer(e, time, unit) |
| 移除 | remove() |
poll() |
take() |
poll(time, unit) |
| 检查队首元素 | element() |
peek() |
无 | 无 |
Q:
peek()和element()有什么区别?答: 两者都是获取队首元素但不移除,区别在于空队列时的处理。
peek()返回null,element()抛出NoSuchElementException。peek()在实际开发中更常用,因为用null判断比捕获异常更简洁。追问:那为什么不统一只用
peek(),还要保留element()?答: 因为
Queue接口继承了AbstractQueue的设计,add/remove/element是抛异常组,offer/poll/peek是返回值组。这是 Java 集合框架的统一设计风格,给开发者两种选择:快速失败或优雅降级。("优雅降级 = 出问题时给你个'备胎',让你继续跑,而不是直接趴窝。")"优雅降级"(Graceful Degradation) 是指:当系统遇到异常情况(如队列为空、服务不可用、资源不足)时,不是直接报错崩溃,而是返回一个"安全默认值"或执行备用逻辑,让系统能够继续运行。
双端阻塞队列的 API
BlockingDeque 在 BlockingQueue 基础上增加了首尾操作:
| 操作 | 抛异常 | 返回特殊值 | 阻塞 | 超时退出 |
|---|---|---|---|---|
| 插入首部 | addFirst(e) |
offerFirst(e) |
putFirst(e) |
offerFirst(e, time, unit) |
| 插入尾部 | addLast(e) |
offerLast(e) |
putLast(e) |
offerLast(e, time, unit) |
| 移除首部 | removeFirst() |
pollFirst() |
takeFirst() |
pollFirst(time, unit) |
| 移除尾部 | removeLast() |
pollLast() |
takeLast() |
pollLast(time, unit) |
| 检查首部 | getFirst() |
peekFirst() |
❌ 无 | ❌ 无 |
| 检查尾部 | getLast() |
peekLast() |
❌ 无 | ❌ 无 |
- 双端阻塞队列 = 单端 API + First/Last 后缀。唯一区别是检查方法叫
getFirst()/getLast(),而不是element。
阻塞队列的 API在 3 个接口层级
| 接口 | 包含的 API | 说明 |
|---|---|---|
Queue |
add(e)、remove()、element()、offer(e)、poll()、peek() |
非阻塞队列的基础接口 |
BlockingQueue |
put(e)、take()、offer(e, time, unit)、poll(time, unit) |
阻塞队列接口,继承 Queue |
Deque |
双端版本(addFirst/addLast 等) |
双端队列接口,继承 Queue |
Queue\AbstractQueue\BlockingQueue
1. 三者关系
Queue (接口)
/ \
implements extends
/ \
AbstractQueue BlockingQueue (接口)
(抽象类) ↑
↑ |
+---- implements -+
|
+---- ArrayBlockingQueue
+---- LinkedBlockingQueue
+---- PriorityBlockingQueue
Queue— 队列根接口,定义add/offer/remove/poll/element/peekBlockingQueue— extendsQueue,增加阻塞方法put/take/offer(time)/poll(time)AbstractQueue— implementsQueue,帮你少写代码的骨架实现(不涉及 BlockingQueue),add/remove/element内部委托给offer/poll/peek
ArrayBlockingQueue、LinkedBlockingQueue、PriorityBlockingQueue同时 extendsAbstractQueue和 implementsBlockingQueue。
2. 线程安全
所有方法都是线程安全的(内部 ReentrantLock 保护),区别在于满/空时的行为:
| 方法 | 队列满时 | 队列空时 |
|---|---|---|
put(e) / take() |
阻塞等待 | 阻塞等待 |
offer(e) / poll() |
返回 false |
返回 null |
add(e) / remove() |
抛异常 | 抛异常 |
offer(e,t) / poll(t) |
超时等待 | 超时等待 |
- 生产者-消费者用
put/take,自动阻塞,优雅高效 - 需要立即知道结果用
offer/poll
常用原子类
原子类基本都是使用CAS保证并发安全的
基本类型原子类
AtomicInteger:原子intAtomicLong:原子longAtomicBoolean:原子boolean
数组原子类
AtomicIntegerArray:原子 int 数组AtomicLongArray:原子 long 数组AtomicReferenceArray<V>: 原子引用数组
引用原子类
AtomicReference<V>:原子引用AtomicStampedReference<V>:带版本号,解决 ABA 问题AtomicMarkableReference<V>:带布尔标记
字段更新器:原子更新某个类的 volatile 且非 private(或通过反射可访问)字段,适用于大量对象实例的场景,节省内存。
AtomicIntegerFieldUpdater<T>: 更新对象 int 字段AtomicLongFieldUpdater<T>: 更新对象 long 字段AtomicReferenceFieldUpdater<T,V>: 更新对象引用字段
累加器(JDK 8+)
LongAdder:高并发 long 累加DoubleAdder:高并发 double 累加LongAccumulator:自定义累加逻辑DoubleAccumulator:自定义 double 累加逻辑
线程执行方法
Runnable:run方法(无返回值,不抛异常)
Callable
同步辅助类
CountDownLatch:等结果、不可重用的、减法器
CyclicBarrier:等人齐、可重用的、加法器
Semaphore:控数量、信号量(控制同时访问的线程数)
| 工具类 | 核心机制 | 通俗类比 | 主要用途 |
|---|---|---|---|
| CountDownLatch | 倒计数器,减到 0 才放行 | 发令枪:教练等所有运动员就位才鸣枪 | 主线程等待多个子线程初始化完成 |
| CyclicBarrier | 循环栅栏,凑齐 N 个线程一起执行 | 旅行团:人齐了导游才带进景区 | N 个线程互相等待,到达屏障点后再一起跑 |
| Semaphore | 信号量,控制同时访问的线程数 | 停车位:有限车位,没位子就得等 | 限流,控制最大并发数(比如数据库连接池) |
锁
synchronized:可重入锁、非公平锁、独占锁
ReentrantLock:可重入锁、可通过传递参数实现公平和非公平锁、独占锁
state为0表示锁空闲,> 0表示锁被占用,且数值代表当前线程的重入次数 。
ReentrantReadWriteLock :可重入锁、可通过传递参数实现公平和非公平锁、读锁(共享锁)、写锁(独占锁)
-
32 位的
state整数一分为二 ,高 16 位管读锁,低 16 位管写锁 -
读锁计数(共享锁数量):共享锁-可重入锁
-
写锁计数(独占锁重入次数):独占锁-可重入锁
函数式接口
1. 什么是函数式接口
函数式接口:只有一个抽象方法的接口,可配合 Lambda 表达式使用。可以有多个 default 方法、static 方法,但抽象方法只能有一个。
@FunctionalInterface // 可选,加了编译器会检查
public interface Runnable {
void run(); // 只有一个抽象方法
}
Runnable r = () -> System.out.println("hello");
2.四大核心函数式接口
| 接口 | 方法 | 输入 | 输出 | 用途 |
|---|---|---|---|---|
Function<T, R> |
R apply(T t) |
T | R | 转换/映射 |
Consumer<T> |
void accept(T t) |
T | 无 | 消费 |
Supplier<T> |
T get() |
无 | T | 提供 |
Predicate<T> |
boolean test(T t) |
T | boolean | 判断 |
Function<String, Integer> len = s -> s.length();
Consumer<String> print = s -> System.out.println(s);
Supplier<Double> random = () -> Math.random();
Predicate<String> isEmpty = s -> s.isEmpty();
3.常见扩展变体
| 接口 | 说明 |
|---|---|
BiFunction<T, U, R> |
两参数 Function |
BiConsumer<T, U> |
两参数 Consumer |
BiPredicate<T, U> |
两参数 Predicate |
UnaryOperator<T> |
Function<T, T>,同类型 |
BinaryOperator<T> |
BiFunction<T, T, T>,同类型 |
IntFunction<R> |
接收 int,返回 R |
ToIntFunction<T> |
接收 T,返回 int |
IntConsumer/LongConsumer/DoubleConsumer |
接收基本类型,避免装箱 |
4. JDK 自带的其他函数式接口
| 接口 | 抽象方法 |
|---|---|
Runnable |
void run() |
Callable<V> |
V call() |
Comparator<T> |
int compare(T, T) |
ActionListener |
void actionPerformed(ActionEvent) |
FileFilter |
boolean accept(File) |
浙公网安备 33010602011771号