JUC相关速记

JUC相关速记

集合

java_collections_overview

接口 一句话总结
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 方法。
      • ConcurrentSkipListSet: 线程安全的 TreeSet(有序 Set),基于ConcurrentSkipListMap实现
  • 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。
  • Queue

    • 线程不安全
      • PriorityQueue:基于二叉堆(优先级堆),按优先级出队(自然或比较器),非 FIFO。
    • 线程安全
      • ArrayBlockingQueue、LinkedBlockingQueue、PriorityBlockingQueue(阻塞队列BlockingQueue 系列):线程安全、阻塞、队列
      • ConcurrentLinkedQueue线程安全、非阻塞、单端(FIFO),底层是链表 + CAS(无锁)。
  • Deque:DequeQueue 的子接口DequeQueue 多了双端操作能力。

    • 线程不安全

      • 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 条)

  1. 默认选 ArrayList + HashMap + HashSet遇到性能瓶颈再优化
  2. 多线程首选 ConcurrentHashMap + BlockingQueue,避开 synchronizedXXX 包装类。
  3. LinkedList 基本可以忘记ArrayDeque 在栈/队列场景下全面替代它。

Concurrent 集合类

Concurrent是 CAS + 锁混合使用的:

  • Concurrent 开头的实现分三类:

    1. 纯 CAS 无锁ConcurrentLinkedQueueConcurrentSkipListMap,性能最高;
    2. CAS + 锁混合ConcurrentHashMap(JDK 8+),桶空用 CAS,桶冲突用 synchronized 锁头节点;
    3. 纯锁CopyOnWriteArrayList(写锁)、BlockingQueue 系列(ReentrantLock),因为阻塞语义必须用锁。
  • 主要手段 是否用锁 锁粒度
    ConcurrentHashMap CAS + synchronized ✅ 用锁 桶级别(仅锁头节点)
    ConcurrentLinkedQueue 纯 CAS ❌ 无锁 无锁(无阻塞算法)
    ConcurrentLinkedDeque 纯 CAS ❌ 无锁 无锁
    ConcurrentSkipListMap 纯 CAS ❌ 无锁 无锁(跳表指针 CAS)
    ConcurrentSkipListSet 纯 CAS ❌ 无锁 内部持有上面的 Map
    CopyOnWriteArrayList ReentrantLock ✅ 用锁 写操作全局锁
    CopyOnWriteArraySet ReentrantLock ✅ 用锁 内部持有上面的 List
    LinkedBlockingQueue ReentrantLock ✅ 用锁 两把锁(takeLock + putLock)
    ArrayBlockingQueue ReentrantLock ✅ 用锁 一把锁(所有操作共用)
    PriorityBlockingQueue ReentrantLock ✅ 用锁 一把锁
    LinkedBlockingDeque ReentrantLock ✅ 用锁 一把锁

面试追问:为什么 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() 返回 nullelement() 抛出 NoSuchElementExceptionpeek() 在实际开发中更常用,因为用 null 判断比捕获异常更简洁。

追问:那为什么不统一只用 peek(),还要保留 element()

答: 因为 Queue 接口继承了 AbstractQueue 的设计,add/remove/element 是抛异常组,offer/poll/peek 是返回值组。这是 Java 集合框架的统一设计风格,给开发者两种选择:快速失败优雅降级。("优雅降级 = 出问题时给你个'备胎',让你继续跑,而不是直接趴窝。")

"优雅降级"(Graceful Degradation) 是指:当系统遇到异常情况(如队列为空、服务不可用、资源不足)时,不是直接报错崩溃,而是返回一个"安全默认值"或执行备用逻辑,让系统能够继续运行

双端阻塞队列的 API

BlockingDequeBlockingQueue 基础上增加了首尾操作

操作 抛异常 返回特殊值 阻塞 超时退出
插入首部 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/peek
  • BlockingQueue — extends Queue,增加阻塞方法 put/take/offer(time)/poll(time)
  • AbstractQueue — implements Queue,帮你少写代码的骨架实现(不涉及 BlockingQueue),add/remove/element 内部委托给 offer/poll/peek

ArrayBlockingQueueLinkedBlockingQueuePriorityBlockingQueue 同时 extends AbstractQueue 和 implements BlockingQueue

2. 线程安全

所有方法都是线程安全的(内部 ReentrantLock 保护),区别在于满/空时的行为:

方法 队列满时 队列空时
put(e) / take() 阻塞等待 阻塞等待
offer(e) / poll() 返回 false 返回 null
add(e) / remove() 抛异常 抛异常
offer(e,t) / poll(t) 超时等待 超时等待
  • 生产者-消费者用 put/take,自动阻塞,优雅高效
  • 需要立即知道结果用 offer/poll

常用原子类

Java 原子类详解 - deyang - 博客园

原子类基本都是使用CAS保证并发安全的

基本类型原子类

  • AtomicInteger:原子int
  • AtomicLong:原子long
  • AtomicBoolean:原子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:call方法(有返回值,抛异常)、用FutureTask将Callable封装成Runnable(FutureTask是Runnable的子类,且构造方法可以传入Callable)

同步辅助类

CountDownLatch:等结果、不可重用的、减法器

CyclicBarrier:等人齐、可重用的、加法器

Semaphore:控数量、信号量(控制同时访问的线程数)

工具类 核心机制 通俗类比 主要用途
CountDownLatch 倒计数器,减到 0 才放行 发令枪:教练等所有运动员就位才鸣枪 主线程等待多个子线程初始化完成
CyclicBarrier 循环栅栏,凑齐 N 个线程一起执行 旅行团:人齐了导游才带进景区 N 个线程互相等待,到达屏障点后再一起跑
Semaphore 信号量,控制同时访问的线程数 停车位:有限车位,没位子就得等 限流,控制最大并发数(比如数据库连接池)

synchronized:可重入锁、非公平锁、独占锁

ReentrantLock:可重入锁、可通过传递参数实现公平和非公平锁、独占锁

  • state0 表示锁空闲,> 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)
posted @ 2026-07-04 16:58  deyang  阅读(7)  评论(0)    收藏  举报