当前位置: 首页 > web >正文

多线程进阶核心知识详解(通俗版)

Java多线程进阶详解

一、锁策略:如何高效管理资源竞争

在多线程环境中,锁是协调资源访问的核心机制。不同的锁策略适用于不同的场景,理解它们的差异能帮助优化程序性能。

1. 乐观锁 vs 悲观锁
  • 悲观锁

    • 核心思想:假设每次访问资源都会发生冲突,因此每次操作前先加锁。

    • 类比:去图书馆借书时,管理员每次都锁上书柜,借阅者需排队等待钥匙。

    • 实现synchronized在竞争激烈时转为悲观锁,依赖操作系统的互斥锁(mutex)。

    • 适用场景:写操作频繁,如银行转账、库存扣减。

  • 乐观锁

    • 核心思想:假设冲突概率低,先操作再检查冲突。

    • 类比:多人编辑在线文档,直接修改内容,提交时检测版本是否冲突。

    • 实现:通过版本号或CAS(Compare and Swap)实现。

// 数据库乐观锁示例(伪SQL)
UPDATE products SET stock = stock - 1, version = version + 1 
WHERE id = 100 AND version = current_version;
  • 适用场景:读多写少,如点赞、评论计数。

2. 自旋锁 vs 挂起等待锁

  • 自旋锁
    像追女神,每天发消息问“在吗?”,直到她分手。

  • 原理:线程通过循环不断尝试获取锁,而非立即挂起。

  • 优点:响应快,避免线程切换开销。

  • 缺点:CPU空转,适合锁持有时间短的场景。

  • 挂起等待锁
    被拒绝后默默等待,直到女神主动联系。

  • 原理:线程获取锁失败后进入阻塞状态,等待被唤醒。

  • 优点:节省CPU资源。

  • 缺点:线程切换开销大,响应延迟。

  • 实现synchronized在竞争激烈时升级为重量级锁,依赖操作系统调度。

3. 公平锁 vs 非公平锁
  • 公平锁:按请求顺序分配锁,避免线程饥饿。

    • 实现ReentrantLock(true)

    • 适用场景:需要严格顺序的业务,如排队系统。

  • 非公平锁:允许插队,提高吞吐量。

    • 实现synchronized和默认的ReentrantLock

    • 适用场景:高并发且任务执行时间差异大,如Web服务器。

4. 可重入锁(递归锁)
  • 核心:同一线程可重复获取同一把锁,避免死锁。

    • 实现synchronizedReentrantLock

    • 示例:递归方法中的加锁操作。

二、CAS机制:无锁编程的魔法

CAS原理
  • 三步操作

    1. 读取内存值V。

    2. 比较V与预期值A。

    3. 若相等,将新值B写入V;否则重试。

1. 什么是CAS?
CAS = 检查并交换。就像自动售货机:

boolean CAS(当前值, 预期值, 新值) {if (当前值 == 预期值) {当前值 = 新值;return true;}return false;
}

2. 原子类实现原理
AtomicInteger为例:

// 伪代码实现i++
public int getAndIncrement() {int oldValue = value;while (!CAS(value, oldValue, oldValue+1)) {oldValue = value; // 值被改了,重试}return oldValue;
}

3. ABA问题与解决

:线程1读取值A,线程2将A→B→A,线程1的CAS操作误判无变化。

  • 问题:你看到瓶子里的水还是满的,但其实已经被倒掉又装满了。

  • 解决:加版本号,修改时检查值和版本号是否一致。

  • Java工具AtomicStampedReference

AtomicStampedReference<Integer> ref = new AtomicStampedReference<>(100, 0);
ref.compareAndSet(100, 200, 0, 1); // 检查值和版本号
三、synchronized的锁升级机制

JVM为优化synchronized性能,设计了锁状态升级策略:

1. 无锁 → 偏向锁
  • 场景:单线程访问,无竞争。

  • 实现:在对象头记录线程ID,减少加锁开销。

  • 类比:办公室的咖啡机默认归你使用,无需每次申请。

2. 偏向锁 → 轻量级锁
  • 场景:多个线程交替访问,竞争较低。

  • 实现:通过CAS自旋尝试获取锁,失败少量次后升级。

  • 代码示例

    synchronized (obj) {// 轻量级锁通过CAS竞争
    }
    3. 轻量级锁 → 重量级锁
  • 场景:高并发竞争,自旋消耗过多CPU。

  • 实现:依赖操作系统的互斥锁(mutex),线程挂起等待。

  • 缺点:用户态与内核态切换开销大。

4. 其他优化
  • 锁消除:JVM检测到不可能存在竞争的锁,直接移除。

    public String concat(String s1, String s2) {StringBuffer sb = new StringBuffer(); // 单线程下锁被消除sb.append(s1);sb.append(s2);return sb.toString();
    }

    锁粗化:合并多个相邻锁操作,减少加锁次数。

  • // 优化前
    synchronized (obj) { doA(); }
    synchronized (obj) { doB(); }
    // 优化后
    synchronized (obj) { doA(); doB(); }
    四、JUC工具类:灵活控制并发

    Java并发包(JUC)提供多种工具类,解决复杂并发问题。

    1. ReentrantLock
  • 核心功能

    • 可中断锁:lockInterruptibly()

    • 超时获取锁:tryLock(long timeout, TimeUnit unit)

    • 公平锁:new ReentrantLock(true)

ReentrantLock lock = new ReentrantLock();
lock.lock();
try {// 临界区代码
} finally {lock.unlock();
}
2. Semaphore(信号量)
  • 作用:控制同时访问资源的线程数。

  • 场景:数据库连接池限流。

  • 示例

  • Semaphore semaphore = new Semaphore(5); // 允许5个线程同时访问
    semaphore.acquire(); // 获取许可
    try {// 使用资源
    } finally {semaphore.release(); // 释放许可
    }
    3. CountDownLatch(倒计时门闩)
  • 作用:等待多个线程完成任务。

  • 场景:启动服务前等待所有组件初始化完成。

  • 示例

  • CountDownLatch latch = new CountDownLatch(3);
    // 线程1、2、3分别执行任务后调用 latch.countDown();
    latch.await(); // 主线程等待所有任务完成
    4. CyclicBarrier(循环栅栏)
  • 作用:多个线程相互等待,达到屏障后同时继续。

  • 场景:多阶段任务并行处理。

  • 示例

  • CyclicBarrier barrier = new CyclicBarrier(3, () -> System.out.println("所有线程到达屏障"));
    // 每个线程调用 barrier.await();
    五、线程池:高效管理线程资源

    线程池通过复用线程减少创建销毁开销,提升系统性能。

    1. 核心参数
  • ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, // 核心线程数(常驻)maximumPoolSize, // 最大线程数(核心+临时)keepAliveTime, // 临时线程空闲存活时间TimeUnit, // 时间单位workQueue, // 任务队列(如LinkedBlockingQueue)threadFactory, // 线程创建工厂rejectedExecutionHandler // 拒绝策略
    );
    2. 工作流程
  • 提交任务,优先由核心线程执行。

  • 核心线程满 → 任务入队列。

  • 队列满 → 创建临时线程。

  • 达到最大线程数 → 触发拒绝策略。

3. 拒绝策略
  • AbortPolicy:抛出RejectedExecutionException(默认)。

  • CallerRunsPolicy:由提交任务的线程执行。

  • DiscardOldestPolicy:丢弃队列中最老的任务。

  • DiscardPolicy:静默丢弃新任务。

4. 创建线程池的四种方式
// 固定线程数
ExecutorService fixedPool = Executors.newFixedThreadPool(10);
// 弹性线程数(无界队列)
ExecutorService cachedPool = Executors.newCachedThreadPool();
// 单线程(任务顺序执行)
ExecutorService singlePool = Executors.newSingleThreadExecutor();
// 定时任务
ScheduledExecutorService scheduledPool = Executors.newScheduledThreadPool(5);
六、线程安全集合类
1. ConcurrentHashMap
  • Java7分段锁:将数据分为16段,每段独立加锁。

  • Java8优化

    • 每个桶独立加锁(链表头节点作为锁对象)。

    • 链表长度≥8时转为红黑树,提高查询效率。

  • 对比Hashtable

    特性HashtableConcurrentHashMap
    锁粒度全表锁桶级锁
    并发性能
    扩容机制单线程扩容多线程协同扩容
2. CopyOnWriteArrayList
  • 原理:写操作时复制新数组,保证读操作无锁。

  • 适用场景:读多写少(如监听器列表)。

  • 代码示例

  • CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
    list.add("item"); // 写时复制
    七、死锁与解决方案
    1. 死锁产生的四个条件
  • 互斥:资源只能被一个线程占用。

  • 不可抢占:资源只能由持有者释放。

  • 请求保持:线程持有资源并等待其他资源。

  • 循环等待:多个线程形成环形等待链。

2. 避免死锁的方法
  • 统一加锁顺序:所有线程按固定顺序获取锁。

// 错误示例:线程1锁A→锁B,线程2锁B→锁A(可能死锁)
// 正确示例:所有线程按A→B顺序加锁
synchronized (lockA) {synchronized (lockB) { /* ... */ }
}

超时机制:使用tryLock设置超时时间。

if (lock.tryLock(1, TimeUnit.SECONDS)) {try { /* ... */ } finally { lock.unlock(); }
}
3. 死锁检测与恢复
  • 工具:使用jstack或JConsole分析线程转储。

  • 恢复:强制终止线程或回滚操作(复杂且少用)。

总结

多线程编程的核心在于合理管理资源竞争与线程协作。关键点包括:

  1. 锁策略选择:根据场景选择乐观锁、悲观锁、公平锁等。

  2. CAS无锁编程:通过原子类避免锁开销,注意ABA问题。

  3. 线程池优化:合理配置参数,平衡资源利用与响应速度。

  4. 安全集合:优先使用ConcurrentHashMapCopyOnWriteArrayList

  5. 死锁预防:统一加锁顺序,使用超时机制。

通过深入理解这些机制,结合实际场景灵活运用,才能构建高效、稳定的并发程序

http://www.xdnf.cn/news/5965.html

相关文章:

  • Python+Streamlit实现登录页
  • python-pyqt6框架工具开发总结
  • PostgreSQL 的表连接方法
  • 25.5.13
  • 2025年金融创新、区块链与信息技术国际会议(FRCIT 2025 2025)
  • 深入解析 I/O 模型:原理、区别与 Java 实践
  • 【Redis 进阶】集群
  • mysql环境配置
  • 锐浪报表 Grid++Report 打印“跨页”文本,解决“文字被中间截断”问题
  • NLTK库: 数据集3-分类与标注语料(Categorized and Tagged Corpora)
  • Ubuntu 24.04 LTS系统上配置国内时间同步
  • “新五强”争锋,基础大模型玩家再洗牌
  • 第十七章 SPI——读写串行FLASH
  • 新华三H3CNE网络工程师认证—路由参数与比较
  • Timsort 算法
  • 基于Win在VSCode部署运行OpenVINO模型
  • FFmpeg多路节目流复用为一路包含多个节目的输出流
  • Vue框架的基本介绍
  • 蓝桥杯13届国B 出差
  • 微服务,服务粒度多少合适
  • 日语学习-日语知识点小记-构建基础-JLPT-N4阶段(20):复习
  • 【docker】--镜像管理
  • 佰力博科技准静态d33测试的注意事项
  • Java基础知识点集合
  • PNG转ico图标(支持圆角矩形/方形+透明背景)Python脚本 - 随笔
  • Java处理压缩文件的两种方式!!!!
  • python通过curl访问deepseek的API调用案例
  • 该如何备考社工考试?
  • 2025年中期大语言模型实力深度剖析
  • Windows系统配置WSL2及Cuda