首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >JavaEE初阶——多线程进阶:从 “锁策略小白” 到 “JUC 高手”,这篇干货文收藏就够了

JavaEE初阶——多线程进阶:从 “锁策略小白” 到 “JUC 高手”,这篇干货文收藏就够了

作者头像
想不明白的过度思考者
发布2025-10-29 16:16:31
发布2025-10-29 16:16:31
10700
举报
文章被收录于专栏:JavaEEJavaEE
运行总次数:0

【深入浅出Java多线程】进阶篇:从锁策略到JUC,一文带你彻底搞懂高并发编程

你以为synchronized只是简单的加锁?Too young, too simple! 本文将带你深入多线程的星辰大海,揭秘高并发编程的底层奥秘

1. 引言:为什么需要“锁策略”?

想象一下,你和你的情敌同时想追到女神。如果没有规则(锁),你们可能会打起来(数据错乱)。而锁策略,就是决定你们如何公平(或不公平)竞争女神的规则。

在多线程世界里,是保证线程安全、维护数据一致性的重要手段。但锁的实现方式多种多样,适用于不同的场景。本文将带你深入探讨常见的锁策略、CAS机制、synchronized原理、JUC常用类等高级主题。


2. 常见的锁策略

🧠 思维导图:锁策略分类
2.1 乐观锁 vs 悲观锁

锁类型

工作方式

适用场景

比喻

悲观锁

总是假设会冲突,先加锁再访问

冲突频繁的场景

约会前先问女神“有空吗?”

乐观锁

假设不会冲突,直接访问,发现冲突再回滚

冲突较少的场景

直接去敲女神门,发现她忙就下次再来

synchronized 初始使用乐观锁,竞争激烈时自动升级为悲观锁。


2.2 重量级锁 vs 轻量级锁

锁的核⼼特性"原子性",这样的机制追根溯源是CPU这样的硬件设备提供的.

  • CPU提供了"原⼦操作指令".
  • 操作系统基于CPU的原⼦指令,实现了mutex 互斥锁.
  • JVM基于操作系统提供的互斥锁,实现了synchronized 和ReentrantLock 等关键字和类.
在这里插入图片描述
在这里插入图片描述

类型

实现方式

性能开销

比喻

重量级锁

依赖操作系统mutex,涉及内核态切换

去银行柜台办业务,排队等待

轻量级锁

用户态完成,CAS实现

自助ATM机,自己操作

synchronized 会从轻量级锁开始,竞争激烈时升级为重量级锁。


2.3 自旋锁

伪代码示例:

代码语言:javascript
代码运行次数:0
运行
复制
while (!tryLock(lock)) {
    // 空循环,不断尝试
}

比喻:

  • 挂起等待锁:表白失败后心灰意冷,多年后女神回头找你
  • 自旋锁:每天早安晚安不停,女神一分手立马接盘

优缺点:

  • ✅ 优点:响应快,不放弃CPU
  • ❌ 缺点:长时间自旋浪费CPU

2.4 公平锁 vs 非公平锁

类型

规则

比喻

公平锁

先来后到,排队执行

先追女神的先上位

非公平锁

谁抢到是谁的

女神看谁顺眼选谁

synchronized非公平锁


2.5 可重入锁

问题: 同一个线程能否多次获取同一把锁?

示例:

代码语言:javascript
代码运行次数:0
运行
复制
synchronized void methodA() {
    methodB(); // 递归或嵌套调用
}

synchronized void methodB() {
    // 不会死锁
}

synchronizedReentrantLock 都是可重入锁。


2.6 读写锁

适用场景:读多写少(如教务系统查看作业)

操作

读锁

写锁

读锁

✅ 不互斥

❌ 互斥

写锁

❌ 互斥

❌ 互斥

Java实现:

代码语言:javascript
代码运行次数:0
运行
复制
ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
rwLock.readLock().lock();   // 加读锁
rwLock.writeLock().lock();  // 加写锁

3. CAS:乐观锁的核心实现

3.1 什么是CAS?

CAS = Compare And Swap,比较并交换。是一个硬件级原子操作

伪代码:

代码语言:javascript
代码运行次数:0
运行
复制
boolean CAS(address, expectValue, swapValue) {
    if (*address == expectValue) {
        *address = swapValue;
        return true;
    }
    return false;
}
3.2 CAS实现原子类
代码语言:javascript
代码运行次数:0
运行
复制
AtomicInteger atomicInt = new AtomicInteger(0);
atomicInt.getAndIncrement(); // 线程安全的i++

内部实现:

代码语言:javascript
代码运行次数:0
运行
复制
public int getAndIncrement() {
    int oldValue = value;
    while (!CAS(value, oldValue, oldValue + 1)) {
        oldValue = value;
    }
    return oldValue;
}
3.3 ABA问题与解决方案

问题: 值从A→B→A,CAS无法感知中间变化。

解决方案: 使用版本号(AtomicStampedReference

代码语言:javascript
代码运行次数:0
运行
复制
AtomicStampedReference<Integer> ref = new AtomicStampedReference<>(100, 1);
ref.compareAndSet(100, 50, 1, 2); // 期望值、新值、期望版本、新版本

4. synchronized 原理详解

4.1 锁升级过程
在这里插入图片描述
在这里插入图片描述
代码语言:javascript
代码运行次数:0
运行
复制
无锁 → 偏向锁 → 轻量级锁(自旋) → 重量级锁

比喻:

  • 偏向锁:只和一个线程好,不领证(不加锁)
  • 轻量级锁:有竞争者,开始自旋等待
  • 重量级锁:竞争激烈,直接排队(挂起)
4.2 锁消除与锁粗化

优化

说明

例子

锁消除

JVM发现不可能竞争,直接去掉锁

单线程下的StringBuffer

锁粗化

连续加锁解锁合并为一次大锁

多次打电话合并为一次交代所有任务


5. JUC常用类详解

5.1 Callable 与 FutureTask

传统方式: 需要手动wait/notify,代码复杂

Callable方式:

代码语言:javascript
代码运行次数:0
运行
复制
Callable<Integer> callable = () -> {
    int sum = 0;
    for (int i = 1; i <= 1000; i++) sum += i;
    return sum;
};

FutureTask<Integer> futureTask = new FutureTask<>(callable);
new Thread(futureTask).start();
int result = futureTask.get(); // 阻塞等待结果

比喻:

  • Callable = 麻辣烫订单
  • FutureTask = 取餐小票

5.2 ReentrantLock
代码语言:javascript
代码运行次数:0
运行
复制
ReentrantLock lock = new ReentrantLock();
lock.lock();
try {
    // 业务代码
} finally {
    lock.unlock();
}

与synchronized对比:

特性

synchronized

ReentrantLock

自动释放

❌ 需手动unlock

可中断

✅ tryLock支持超时

公平锁

✅ 可配置

条件变量

✅ Condition精准唤醒


5.3 线程池(ThreadPoolExecutor)

构造参数详解(公司模型):

参数

比喻

说明

corePoolSize

正式工

核心线程数,永不辞退

maximumPoolSize

正式工+临时工

最大线程数

keepAliveTime

临时工空闲时间

超过则辞退

workQueue

任务队列

存放待执行任务

handler

拒单策略

任务太多时的处理方式

示例:

代码语言:javascript
代码运行次数:0
运行
复制
ExecutorService pool = new ThreadPoolExecutor(
    2,  // 核心线程数
    5,  // 最大线程数
    60, // 空闲时间
    TimeUnit.SECONDS,
    new LinkedBlockingQueue<>(10),
    new ThreadPoolExecutor.AbortPolicy()
);

5.4 信号量(Semaphore)

比喻: 停车场空位计数器

代码语言:javascript
代码运行次数:0
运行
复制
Semaphore semaphore = new Semaphore(4); // 4个车位

semaphore.acquire(); // P操作,申请资源
// 访问资源
semaphore.release(); // V操作,释放资源

5.5 CountDownLatch

比喻: 跑步比赛,所有人到达终点才公布成绩

代码语言:javascript
代码运行次数:0
运行
复制
CountDownLatch latch = new CountDownLatch(10);
for (int i = 0; i < 10; i++) {
    new Thread(() -> {
        // 完成任务
        latch.countDown();
    }).start();
}
latch.await(); // 等待所有任务完成
System.out.println("全部完成!");

6. 线程安全集合类

6.1 ConcurrentHashMap vs Hashtable

特性

Hashtable

ConcurrentHashMap

锁粒度

整个表一把锁

每个桶一把锁

性能

读写

全锁

读无锁,写锁桶

ConcurrentHashMap 在JDK8中改为:数组 + 链表/红黑树 + CAS + synchronized


7. 死锁与避免

7.1 死锁产生的四个条件
  1. 互斥使用
  2. 不可抢占
  3. 请求并保持
  4. 循环等待
7.2 破坏死锁:锁排序

错误示例:

代码语言:javascript
代码运行次数:0
运行
复制
// 线程1
synchronized (lockA) {
    synchronized (lockB) { ... }
}

// 线程2
synchronized (lockB) {
    synchronized (lockA) { ... } // 可能死锁
}

正确示例: 约定获取锁的顺序(如按lockA→lockB)


8. 常见面试题速览

问题

答案要点

synchronized 可重入吗?

✅ 是,递归不会死锁

CAS 的 ABA 问题如何解决?

使用版本号(AtomicStampedReference)

线程池参数含义?

核心数、最大数、队列、拒绝策略

volatile 作用?

保证可见性,禁止指令重排

死锁如何避免?

锁排序、超时机制、避免嵌套锁


9. 总结

多线程编程是一门艺术,更是工程实践中的必备技能。从基础的synchronized到强大的JUC工具包,从锁策略到CAS无锁编程,每一个知识点都是为了在安全性能之间找到最佳平衡点。

记住: 没有最好的锁,只有最合适的锁。理解原理,结合实际场景,才能写出高效稳定的并发程序。

参考资料:

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-09-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 【深入浅出Java多线程】进阶篇:从锁策略到JUC,一文带你彻底搞懂高并发编程
    • 1. 引言:为什么需要“锁策略”?
    • 2. 常见的锁策略
      • 🧠 思维导图:锁策略分类
      • 2.1 乐观锁 vs 悲观锁
      • 2.2 重量级锁 vs 轻量级锁
      • 2.3 自旋锁
      • 2.4 公平锁 vs 非公平锁
      • 2.5 可重入锁
      • 2.6 读写锁
    • 3. CAS:乐观锁的核心实现
      • 3.1 什么是CAS?
      • 3.2 CAS实现原子类
      • 3.3 ABA问题与解决方案
    • 4. synchronized 原理详解
      • 4.1 锁升级过程
      • 4.2 锁消除与锁粗化
    • 5. JUC常用类详解
      • 5.1 Callable 与 FutureTask
      • 5.2 ReentrantLock
      • 5.3 线程池(ThreadPoolExecutor)
      • 5.4 信号量(Semaphore)
      • 5.5 CountDownLatch
    • 6. 线程安全集合类
      • 6.1 ConcurrentHashMap vs Hashtable
    • 7. 死锁与避免
      • 7.1 死锁产生的四个条件
      • 7.2 破坏死锁:锁排序
    • 8. 常见面试题速览
    • 9. 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档