Java并发核心机制详解:线程池、CAS、AQS、锁升级

Java并发核心机制详解:线程池、CAS、AQS、锁升级

1. 线程池(ThreadPool)

1.1 为什么需要线程池

问题场景

  • 频繁创建和销毁线程带来巨大开销
  • 无限制创建线程导致内存溢出
  • 缺乏统一管理,难以监控和调优

线程池的优势

  1. 降低资源消耗:复用线程,减少创建/销毁开销
  2. 提高响应速度:任务到达时无需等待线程创建
  3. 提高可管理性:统一管理、监控、调优
  4. 提高可扩展性:支持动态调整线程数量

1.2 线程池核心参数

java 复制代码
public ThreadPoolExecutor(
    int corePoolSize,        // 核心线程数
    int maximumPoolSize,     // 最大线程数
    long keepAliveTime,      // 空闲线程存活时间
    TimeUnit unit,           // 时间单位
    BlockingQueue<Runnable> workQueue,  // 任务队列
    ThreadFactory threadFactory,        // 线程工厂
    RejectedExecutionHandler handler    // 拒绝策略
)

1.3 线程池工作流程

复制代码
提交任务
    ↓
┌─────────────────────────────────────────────────────────┐
│  1. 核心线程数未满?                                      │
│     ├── 是 → 创建核心线程执行任务                         │
│     └── 否 → 继续                                       │
├─────────────────────────────────────────────────────────┤
│  2. 工作队列未满?                                        │
│     ├── 是 → 任务加入队列等待                             │
│     └── 否 → 继续                                       │
├─────────────────────────────────────────────────────────┤
│  3. 线程数未达最大值?                                    │
│     ├── 是 → 创建非核心线程执行任务                       │
│     └── 否 → 执行拒绝策略                                │
└─────────────────────────────────────────────────────────┘

1.4 四种拒绝策略

拒绝策略 行为 适用场景
AbortPolicy(默认) 抛出RejectedExecutionException 不允许丢失任务
CallerRunsPolicy 由提交任务的线程执行 允许降级执行
DiscardPolicy 静默丢弃任务 可容忍丢失
DiscardOldestPolicy 丢弃队列中最老的任务 优先执行新任务
java 复制代码
// 自定义拒绝策略
public class CustomRejectionHandler implements RejectedExecutionHandler {
    @Override
    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
        // 记录日志、降级处理等
        log.warn("任务被拒绝: " + r.toString());
    }
}

1.5 常见线程池类型

1.5.1 FixedThreadPool(固定数量线程池)
java 复制代码
ExecutorService pool = Executors.newFixedThreadPool(10);
  • 核心线程数 = 最大线程数 = 固定值
  • 使用LinkedBlockingQueue(无界队列)
  • 风险:任务堆积导致OOM
1.5.2 SingleThreadExecutor(单线程池)
java 复制代码
ExecutorService pool = Executors.newSingleThreadExecutor();
  • 核心线程数 = 最大线程数 = 1
  • 保证任务按顺序执行
  • 风险:任务堆积导致OOM
1.5.3 CachedThreadPool(缓存线程池)
java 复制代码
ExecutorService pool = Executors.newCachedThreadPool();
  • 核心线程数 = 0,最大线程数 = Integer.MAX_VALUE
  • 使用SynchronousQueue(不存储元素)
  • 风险:创建大量线程导致OOM
1.5.4 ScheduledThreadPool(定时线程池)
java 复制代码
ScheduledExecutorService pool = Executors.newScheduledThreadPool(5);
  • 支持定时和周期性任务
  • 使用DelayedWorkQueue

1.6 线程池参数设置经验

参数 推荐值 说明
CPU密集型任务 N+1(N为CPU核数) 减少上下文切换
IO密集型任务 2N 或 N/(1-阻塞系数) 充分利用IO等待时间
混合型任务 根据压测调整 可拆分为CPU和IO子任务

动态线程池实现

java 复制代码
// 动态调整核心线程数和最大线程数
public class DynamicThreadPool {
    private final ThreadPoolExecutor executor;
    
    public void updateCorePoolSize(int coreSize) {
        executor.setCorePoolSize(coreSize);
    }
    
    public void updateMaxPoolSize(int maxSize) {
        executor.setMaximumPoolSize(maxSize);
    }
}

2. CAS(Compare And Swap)

2.1 CAS基本概念

CAS是一种乐观锁机制,包含三个操作数:

  • V(内存值):要更新的变量
  • E(期望值):期望的旧值
  • N(新值):要设置的新值

操作语义:当且仅当V == E时,将V的值设置为N,否则不做任何操作。

2.2 CAS底层实现

java 复制代码
// AtomicInteger的incrementAndGet方法
public final int incrementAndGet() {
    return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}

// Unsafe类的getAndAddInt方法
public final int getAndAddInt(Object obj, long offset, int delta) {
    int expected;
    do {
        expected = this.getIntVolatile(obj, offset);  // 获取当前值
    } while (!this.compareAndSwapInt(obj, offset, 
                                      expected, 
                                      expected + delta));  // CAS更新
    return expected;
}

2.3 CAS的三大问题

2.3.1 ABA问题

问题描述:值从A变为B再变回A,CAS会误认为没有变化。

复制代码
线程1:读取V=A
线程2:V=A → V=B
线程3:V=B → V=A
线程1:CAS更新(期望A,实际A,但中间经历过变化)

解决方案

java 复制代码
// 使用AtomicStampedReference(版本号)
AtomicStampedReference<Integer> ref = 
    new AtomicStampedReference<>(1, 0);
int stamp = ref.getStamp();  // 获取版本号
ref.compareAndSet(1, 2, stamp, stamp + 1);

// 使用AtomicMarkableReference(布尔标记)
AtomicMarkableReference<Integer> ref = 
    new AtomicMarkableReference<>(1, false);
2.3.2 自旋开销问题

问题描述:CAS失败后会一直自旋重试,在高并发场景下消耗大量CPU。

解决方案

  1. 使用LongAdder代替AtomicLong(分段CAS)
  2. 减小CAS粒度
  3. 使用锁代替CAS
2.3.3 只能保证一个变量的原子操作

问题描述:CAS一次只能操作一个变量。

解决方案

java 复制代码
// 使用AtomicReference封装多个变量
class Pair {
    int x, y;
    // ...
}
AtomicReference<Pair> ref = new AtomicReference<>(new Pair(0, 0));

// 使用锁保证多变量原子性
synchronized(lock) {
    // 同时更新多个变量
}

2.4 CAS应用场景

场景 说明
原子计数器 AtomicInteger、AtomicLong 高并发计数
无锁队列 ConcurrentLinkedQueue 高并发队列
无锁栈 ConcurrentLinkedStack 高并发栈
比较交换 AtomicReference 引用类型原子操作

3. AQS(AbstractQueuedSynchronizer)

3.1 AQS核心概念

AQS是Java并发包的基石,ReentrantLock、Semaphore、CountDownLatch等都是基于AQS实现。

核心思想

  • 维护一个volatile int state表示同步状态
  • 通过CLH(Craig, Landin, and Hagersten)队列管理等待线程

3.2 AQS工作原理

复制代码
┌─────────────────────────────────────────────────────────┐
│                    AQS核心结构                           │
├─────────────────────────────────────────────────────────┤
│  volatile int state;  // 同步状态                       │
│  └── 0:未锁定                                          │
│  └── >0:已锁定(可重入次数)                            │
├─────────────────────────────────────────────────────────┤
│  CLH双向队列                                            │
│  ┌──────┐  ┌──────┐  ┌──────┐                         │
│  │ head │→ │ Node │→ │ Node │→ ...                    │
│  └──────┘  └──────┘  └──────┘                         │
│  代表持有锁的线程    等待获取锁的线程                     │
└─────────────────────────────────────────────────────────┘

3.3 独占锁获取流程

java 复制代码
// acquire方法(以ReentrantLock为例)
public final void acquire(int arg) {
    if (!tryAcquire(arg) &&                          // 尝试获取锁
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))  // 入队等待
        selfInterrupt();
}

// tryAcquire实现(非公平锁)
final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {  // 锁未被占用
        if (compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    } else if (current == getExclusiveOwnerThread()) {
        // 可重入
        setState(c + acquires);
        return true;
    }
    return false;
}

3.4 公平锁 vs 非公平锁

特性 公平锁 非公平锁
加锁顺序 按照请求顺序 允许插队
实现方式 hasQueuedPredecessors() 无额外检查
吞吐量 较低 较高
适用场景 需要公平性 性能优先
java 复制代码
// 公平锁实现
protected final boolean tryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        // 公平锁会检查队列中是否有等待线程
        if (!hasQueuedPredecessors() &&
            compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    // ...
}

3.5 共享锁获取流程

java 复制代码
// acquireShared方法(以CountDownLatch为例)
public final void acquireShared(int arg) {
    if (tryAcquireShared(arg) < 0)  // 尝试获取共享锁
        doAcquireShared(arg);       // 入队等待
}

// CountDownLatch的tryAcquireShared实现
protected int tryAcquireShared(int acquires) {
    return (getState() == 0) ? 1 : -1;
    // state为0时返回1(成功),否则返回-1(失败)
}

3.6 Condition条件队列

java 复制代码
// ReentrantLock的Condition
ReentrantLock lock = new ReentrantLock();
Condition notFull = lock.newCondition();
Condition notEmpty = lock.newCondition();

// 生产者
lock.lock();
try {
    while (queue.isFull()) {
        notFull.await();  // 进入条件队列等待
    }
    // 生产数据
    notEmpty.signal();  // 唤醒消费者
} finally {
    lock.unlock();
}

3.7 AQS核心方法

方法 说明 独占/共享
tryAcquire(arg) 尝试获取独占锁 独占
tryRelease(arg) 尝试释放独占锁 独占
tryAcquireShared(arg) 尝试获取共享锁 共享
tryReleaseShared(arg) 尝试释放共享锁 共享
isHeldExclusively() 是否独占 独占

4. 锁升级机制

4.1 Java对象头

复制代码
┌─────────────────────────────────────────────────────────┐
│                    64位JVM对象头                         │
├─────────────────────────────────────────────────────────┤
│  Mark Word (8字节)                                      │
│  ├── 无锁:hashcode(31) | age(4) | 偏向(1) = 0        │
│  ├── 偏向锁:threadId(54) | age(4) | 偏向(1) = 1      │
│  ├── 轻量级锁:指向栈中锁记录的指针(62) | 锁标志(2)     │
│  ├── 重量级锁:指向Monitor的指针(62) | 锁标志(2)        │
│  └── GC标记:空                                       │
├─────────────────────────────────────────────────────────┤
│  Klass Pointer (4字节) - 指向类元数据                   │
│  + 4字节对齐填充                                        │
└─────────────────────────────────────────────────────────┘

4.2 锁升级流程

复制代码
┌─────────────────────────────────────────────────────────┐
│                    锁升级流程                            │
├─────────────────────────────────────────────────────────┤
│  无锁 → 偏向锁 → 轻量级锁 → 重量级锁                    │
│    ↑         ↑          ↑          ↑                    │
│  初始状态  首次加锁    竞争出现    竞争激烈               │
└─────────────────────────────────────────────────────────┘

4.3 偏向锁

4.3.1 偏向锁原理
  • 只有一个线程访问同步块时启用
  • 在对象头Mark Word中记录线程ID
  • 下次同一线程访问时无需任何同步操作
4.3.2 偏向锁获取流程
复制代码
1. 检查对象头Mark Word是否存储当前线程ID
   ├── 是 → 直接进入同步块(无需CAS)
   └── 否 → 继续
2. CAS将线程ID设置到Mark Word
   ├── 成功 → 获取偏向锁
   └── 失败 → 撤销偏向锁,升级为轻量级锁
4.3.3 偏向锁撤销
  • 批量重偏向:当撤销次数超过阈值(默认20),将新线程ID偏向
  • 批量撤销:当撤销次数超过阈值(默认40),所有对象变成无锁状态

4.4 轻量级锁

4.4.1 轻量级锁原理
  • 多个线程交替访问同步块时启用
  • 通过CAS操作Mark Word实现
  • 不阻塞线程,自旋重试
4.4.2 轻量级锁加锁流程
复制代码
1. 在线程栈帧中创建锁记录(Lock Record)
2. CAS将Mark Word替换为指向锁记录的指针
   ├── 成功 → 获取轻量级锁
   └── 失败 → 自旋重试
3. 自旋失败超过阈值 → 升级为重量级锁
4.4.3 自旋优化
  • 自适应自旋:根据上次自旋结果动态调整自旋次数
  • 锁消除:JIT编译器消除不可能存在竞争的锁
  • 锁粗化:将多个连续的同步块合并为一个

4.5 重量级锁

4.5.1 重量级锁原理
  • 多个线程同时访问同步块时启用
  • 依赖操作系统Mutex Lock实现
  • 线程会被阻塞和唤醒,涉及内核态/用户态切换
4.5.2 Monitor结构
复制代码
┌─────────────────────────────────────────────────────────┐
│                    Monitor对象                           │
├─────────────────────────────────────────────────────────┤
│  Owner          →  持有锁的线程                         │
│  EntryList      →  等待获取锁的线程队列                  │
│  WaitSet        →  调用wait()等待的线程                 │
│  recursions     →  锁重入次数                           │
└─────────────────────────────────────────────────────────┘

4.6 锁升级总结

锁状态 存储内容 适用场景 性能开销
偏向锁 线程ID 单线程访问 最低
轻量级锁 栈帧锁记录指针 线程交替访问 较低
重量级锁 Monitor指针 多线程同时访问 最高

4.7 锁消除与锁粗化

4.7.1 锁消除

JIT编译器通过逃逸分析,消除不可能存在竞争的锁。

java 复制代码
// 锁消除前
public String concat(String s1, String s2) {
    StringBuffer sb = new StringBuffer();  // 每次创建新对象
    sb.append(s1);
    sb.append(s2);
    return sb.toString();
}

// 锁消除后:StringBuffer的锁被消除
// 因为sb是局部变量,不可能逃逸出方法
4.7.2 锁粗化

将多个连续的同步块合并为一个,减少加锁/解锁次数。

java 复制代码
// 锁粗化前
for (int i = 0; i < 100; i++) {
    synchronized (lock) {
        // 每次循环都加锁/解锁
    }
}

// 锁粗化后
synchronized (lock) {
    for (int i = 0; i < 100; i++) {
        // 只加锁一次
    }
}

5. 企业级面试题

5.1 线程池相关

Q1:线程池的核心参数有哪些?各自的作用是什么?

A:核心参数有7个:

  • corePoolSize:核心线程数,即使空闲也会保留
  • maximumPoolSize:最大线程数
  • keepAliveTime:空闲线程最大存活时间
  • unit:时间单位
  • workQueue:任务队列,存放等待执行的任务
  • threadFactory:线程工厂,创建线程
  • handler:拒绝策略,队列满且线程数达最大值时的处理方式

Q2:线程池的任务执行流程是什么?

A:

  1. 提交任务时,如果核心线程数未满,创建核心线程执行
  2. 如果核心线程数已满,将任务加入工作队列
  3. 如果工作队列已满,检查线程数是否达到最大值
  4. 如果未达到最大值,创建非核心线程执行
  5. 如果达到最大值,执行拒绝策略

Q3:为什么不推荐使用Executors创建线程池?

A:因为Executors创建的线程池存在OOM风险:

  • FixedThreadPoolSingleThreadExecutor使用无界LinkedBlockingQueue,任务堆积会OOM
  • CachedThreadPool使用SynchronousQueue,最大线程数为Integer.MAX_VALUE,可能创建大量线程OOM
  • 建议使用ThreadPoolExecutor手动创建,明确参数

Q4:如何合理配置线程池参数?

A:根据任务类型配置:

  • CPU密集型:核心线程数 = CPU核数 + 1
  • IO密集型:核心线程数 = 2 × CPU核数 或 CPU核数 / (1 - 阻塞系数)
  • 混合型:通过压测调整,或拆分为CPU和IO子任务

实际项目中建议通过配置中心动态调整,如美团的动态线程池方案。


Q5:线程池的拒绝策略有哪些?如何自定义?

A:四种内置策略:

  • AbortPolicy:抛出RejectedExecutionException(默认)
  • CallerRunsPolicy:由提交任务的线程执行
  • DiscardPolicy:静默丢弃任务
  • DiscardOldestPolicy:丢弃队列中最老的任务

自定义需要实现RejectedExecutionHandler接口,重写rejectedExecution方法。


Q6:线程池如何实现优雅关闭?

A:使用shutdown()和awaitTermination():

java 复制代码
executor.shutdown();  // 停止接收新任务,等待已提交任务完成
try {
    if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
        executor.shutdownNow();  // 超时强制关闭
        if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
            System.err.println("线程池未完全关闭");
        }
    }
} catch (InterruptedException e) {
    executor.shutdownNow();
    Thread.currentThread().interrupt();
}

5.2 CAS相关

Q7:什么是CAS?它的底层实现原理是什么?

A:CAS(Compare And Swap)是一种乐观锁机制,包含三个操作数:内存值V、期望值E、新值N。

底层通过Unsafe类的native方法实现,使用CPU的原子指令(如x86的CMPXCHG)保证比较和交换的原子性。


Q8:CAS存在哪些问题?如何解决?

A:三大问题及解决方案:

  • ABA问题:使用AtomicStampedReference(版本号)或AtomicMarkableReference(布尔标记)
  • 自旋开销:使用LongAdder代替AtomicLong(分段CAS)
  • 单变量限制:使用AtomicReference封装多个变量,或使用锁

Q9:AtomicLong和LongAdder有什么区别?

A:

  • AtomicLong:基于CAS,高并发时自旋竞争激烈
  • LongAdder:分段CAS,将单点更新分散到多个单元
  • LongAdder在高并发下性能更好,但不保证实时一致性
  • 适合统计场景,不适合需要精确值的场景

Q10:CAS和synchronized有什么区别?

A:

特性 CAS synchronized
实现方式 乐观锁 悲观锁
是否阻塞 否,自旋重试 是,线程阻塞
适用场景 低竞争 高竞争
性能 低竞争时好 高竞争时好
保证 原子性 原子性、可见性、有序性

5.3 AQS相关

Q11:什么是AQS?它的核心原理是什么?

A:AQS(AbstractQueuedSynchronizer)是Java并发包的基石,ReentrantLock、Semaphore、CountDownLatch等都是基于AQS实现。

核心原理:

  • 维护volatile int state表示同步状态
  • 通过CLH双向队列管理等待线程
  • 子类重写tryAcquire/tryRelease实现独占锁
  • 子类重写tryAcquireShared/tryReleaseShared实现共享锁

Q12:ReentrantLock是如何基于AQS实现的?

A:

  • state:表示锁的重入次数,0表示未锁定
  • exclusiveOwnerThread:记录持有锁的线程
  • 加锁时CAS将state从0改为1,重入时state+1
  • 解锁时state-1,直到state=0释放锁
  • 公平锁通过hasQueuedPredecessors()检查队列中是否有等待线程

Q13:CountDownLatch和CyclicDownLatch有什么区别?

A:

特性 CountDownLatch CyclicBarrier
用途 一个/多个线程等待其他线程完成 多个线程互相等待到齐
重用 一次性 可重用(reset)
实现 AQS共享模式 ReentrantLock + Condition
场景 主线程等待子任务完成 多线程分阶段计算

Q14:Semaphore是如何实现许可控制的?

A:

  • 初始化时设置state为许可数量
  • acquire():CAS将state减1,失败则阻塞
  • release():CAS将state加1,唤醒等待线程
  • 公平模式使用FIFO队列,非公平模式允许插队

Q15:AQS中的CLH队列是什么?有什么特点?

A:CLH是一种双向链表队列,由Craig、Landin和Hagersten提出。

特点:

  • 虚拟队列,通过head和tail指针管理
  • 节点包含线程引用、等待状态、前驱/后继指针
  • 入队通过CAS保证原子性
  • 出队时通过自旋检查前驱节点状态
  • 支持中断和超时

5.4 锁升级相关

Q16:Java中的锁有哪几种状态?

A:四种状态(从低到高):

  • 无锁:没有线程访问同步块
  • 偏向锁:只有一个线程访问
  • 轻量级锁:多个线程交替访问(无竞争)
  • 重量级锁:多个线程同时访问(有竞争)

锁只能升级不能降级(偏向锁除外)。


Q17:偏向锁的原理是什么?什么情况下会撤销?

A:偏向锁原理:

  • 在对象头Mark Word中记录线程ID
  • 同一线程再次访问时无需CAS,直接比较线程ID
  • 只有一个线程访问时性能最优

撤销条件:

  • 撤销次数超过阈值(批量重偏向/批量撤销)
  • 其他线程尝试获取偏向锁
  • 调用对象的hashcode()方法(会撤销偏向状态)

Q18:轻量级锁的加锁过程是什么?

A:

  1. 在线程栈帧中创建锁记录(Lock Record)
  2. CAS将对象头Mark Word替换为指向锁记录的指针
  3. 成功则获取轻量级锁
  4. 失败则自旋重试
  5. 自旋超过阈值,升级为重量级锁

Q19:什么情况下会触发锁升级?

A:

  • 偏向锁 → 轻量级锁:其他线程尝试获取偏向锁时
  • 轻量级锁 → 重量级锁:自旋超过阈值或竞争激烈时
  • 无锁 → 轻量级锁:直接发生竞争时跳过偏向锁

Q20:锁消除和锁粗化是什么?有什么作用?

A:

  • 锁消除:JIT编译器通过逃逸分析,消除不可能存在竞争的锁

    • 如StringBuffer.append()的锁被消除
    • 作用:减少不必要的同步开销
  • 锁粗化:将多个连续的同步块合并为一个

    • 如for循环中的同步块合并
    • 作用:减少加锁/解锁次数

5.5 综合实战题

Q21:如何设计一个高并发的计数器?

A:根据场景选择方案:

  • 高并发读、低写入:使用LongAdder(分段CAS)
  • 需要精确值:使用AtomicLong(CAS)
  • 需要多变量原子操作:使用AtomicReference或锁
  • 分布式环境:使用Redis INCR或数据库乐观锁

Q22:线上服务CPU飙高,如何排查是否与锁有关?

A:

  1. 使用top找到CPU高的进程PID
  2. 使用top -Hp PID找到CPU高的线程TID
  3. 使用jstack PID导出线程堆栈
  4. 检查是否有大量线程在等待锁(BLOCKED/WAITING状态)
  5. 分析锁竞争热点,优化锁粒度或使用无锁方案

Q23:如何避免死锁?

A:

  1. 固定加锁顺序:所有线程按相同顺序获取锁
  2. 设置超时时间:使用tryLock(timeout)避免无限等待
  3. 避免嵌套锁:减少锁的持有时间
  4. 使用无锁方案:CAS、ConcurrentHashMap等
  5. 死锁检测:使用jstack或ThreadMXBean检测

Q24:ConcurrentHashMap是如何实现高并发的?

A:JDK 8实现:

  • 数据结构:数组 + 链表 + 红黑树
  • 分段锁:每个桶独立加锁,锁粒度为Node
  • CAS操作:插入空桶时使用CAS
  • synchronized:链表/红黑树操作使用synchronized(锁升级优化)
  • size():使用baseCount + CounterCell数组分散计数

Q25:AQS在实际项目中有哪些应用?

A:

  • ReentrantLock:可重入互斥锁,替代synchronized
  • ReadWriteLock:读写锁,提高读多写少场景性能
  • Semaphore:限流、资源控制
  • CountDownLatch:多线程同步、等待任务完成
  • CyclicBarrier:多线程分阶段计算
  • Phaser:灵活的分阶段同步(JDK 7+)

6. 总结

6.1 核心要点速查

机制 核心原理 关键点
线程池 复用线程执行任务 7个核心参数、执行流程、拒绝策略
CAS 乐观锁,原子比较交换 ABA问题、自旋开销、Unsafe实现
AQS state + CLH队列 独占/共享模式、公平/非公平锁
偏向锁 记录线程ID 单线程优化、批量重偏向
轻量级锁 CAS + 自旋 交替访问、自适应自旋
重量级锁 Monitor + 阻塞 高竞争场景、性能开销大

6.2 面试答题技巧

  1. 原理先行:先说核心原理,再展开细节
  2. 对比分析:用表格对比相似概念
  3. 结合场景:说明适用场景和选择依据
  4. 举例说明:用代码或图示辅助解释
  5. 关注细节:如AQS的state含义、锁的存储结构

6.3 技术选型建议

场景 推荐方案
高并发计数 LongAdder
限流控制 Semaphore
任务编排 CountDownLatch/CyclicBarrier
读写分离 ReadWriteLock
锁升级优化 偏向锁 → 轻量级锁
相关推荐
逆境不可逃1 小时前
Pi Agent 学习笔记:一次提问背后的工具调用循环
java·笔记·学习
hahaha60161 小时前
HLS高层次综合设计技巧--数组
开发语言·嵌入式硬件·算法·fpga开发
aosky1 小时前
Linux 写内容到文件的几种方式
java·linux·服务器
佳児素花痴╮1 小时前
Qt速通2
开发语言·qt
Wang's Blog1 小时前
Java框架快速入门: Spring Security+OAuth2之邮件验证码发送(SMTP与API方式)
java·数据库·spring
“AI国潮设计-小江”2 小时前
【Python/SDXL实战】潮汕国潮IP视觉落地:普宁英歌舞猫IP & 创意甜品设计(附ComfyUI工作流与商业授权说明)
开发语言·人工智能·python·prompt·aigc
529宝宝起名网2 小时前
用 Python 分析汉字字源与起名用字的关联规律:从六书结构到文化寓意的起名偏好洞察
开发语言·python
linx2952 小时前
单元九 · 零基础路线图-第 7–9 周·类与 RAII
c语言·开发语言·数据结构·c++·嵌入式硬件·算法
砚底藏山河2 小时前
容错重试与指数退避:网络抖动手抖不再丢数据(魔码量化实战 #04)
java·数据库·python·金融