开始改变第三天 Java并发(1)

深入Java并发世界:从核心概念到实战精粹 | 技术笔记

近日系统性地重温了Java并发编程,将核心知识模块梳理为五个关键部分。这不仅是一次学习记录,更愿成为你探索并发秘境时的一盏引路之灯。

引言:为何并发如此重要?

在多核处理器成为主流的今天,能否充分利用硬件资源,构建高效、可靠的高并发应用,已成为衡量一名后端开发者功底的关键。然而,并发编程并非坦途,它充满了线程安全、死锁、内存可见性、原子性等诸多挑战。理解其内在机理,是驯服这头"猛兽"的不二法门。

本文将围绕五大核心知识点,带你由表及里,构建坚实的Java并发知识体系。


第一部分:并发基石 ------ 线程基础与核心概念

核心思想: 万物皆有源,理解并发必须先理解线程本身。

  • 线程的创建与生命周期:

    • 继承 Thread 类 vs. 实现 Runnable / Callable 接口。后者更佳,因为实现了接口分离,便于任务共享。
    • 线程状态:NEW(新建)、RUNNABLE(可运行)、BLOCKED(阻塞)、WAITING(等待)、TIMED_WAITING(超时等待)、TERMINATED(终止)。深刻理解状态转换是诊断多线程问题的基础。
  • 关键概念:

    • 线程安全: 当多个线程访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他协调操作,调用这个对象的行为都可以获得正确的结果,那就称这个对象是线程安全的。
    • 竞态条件: 计算的正确性取决于多个线程的交替执行时序。
    • 共享与同步: 堆内存、方法区内存是线程共享的,而栈内存和程序计数器是线程私有的。为了解决共享带来的问题,引入了 synchronized 等同步机制。

学习心得: 这一部分是所有并发知识的"地基",务必清晰地理解每个状态的意义和转换条件,这是后续分析复杂锁问题(如死锁)的前提。


第二部分:内存模型的灵魂 ------ 深入理解MESI与volatile

核心思想: 解决可见性与有序性问题,必须深入到CPU缓存和内存屏障的层面。

  • JMM(Java内存模型): JMM是一个抽象规范,它规定了线程如何以及何时可以看到其他线程修改过的共享变量,以及在必要时如何同步地访问共享变量。它定义了主内存工作内存之间的关系。

  • MESI协议: 这是理解 volatile 和 synchronized 底层实现的钥匙。

    • 它是一种CPU缓存一致性协议。CPU缓存行有四种状态:
      • M(Modified,修改): 缓存行是脏的,与主内存值不同。
      • E(Exclusive,独占): 缓存行是干净的,与主内存一致,且只有本核心有副本。
      • S(Shared,共享): 缓存行是干净的,与主内存一致,但多个核心可能有副本。
      • I(Invalid,无效): 缓存行数据无效,不能使用。
    • 通过这套状态机协议,CPU在读写数据时,通过总线消息来协调,保证了多个核心缓存数据的一致性。
  • volatile关键字:

    • 语义1:保证可见性。 对一个volatile变量的写,会立刻刷新回主内存,并导致其他CPU中对应的缓存行失效,迫使其他线程在读取时必须从主内存重新加载。
    • 语义2:禁止指令重排序。 通过插入内存屏障 来实现。
    • 底层实现: 正是JVM在编译时在volatile读写操作前后加入特定内存屏障,这些屏障会触发CPU的MESI协议或类似机制,从而实现了上述两大语义。

学习心得: 以前只知道volatile能保证可见性,但不知其所以然。理解了MESI和内存屏障后,才真正明白它的魔力来源。它不保证原子性 ,所以不适合 i++ 这样的场景。


第三部分:锁的艺术 ------ 精通ReentrantLock

核心思想: synchronized 是"自动挡",而 ReentrantLock 是"手动挡",提供了更灵活、更强大的锁控制。

  • 与synchronized对比:

    • 可中断: lockInterruptibly() 可以响应中断,避免死锁。
    • 尝试非阻塞: tryLock() 可以立即返回,避免长时间等待。
    • 公平性: 可以创建公平锁(按申请顺序获取锁),虽然通常性能较低。
    • 绑定多个条件: 一个 ReentrantLock 可以绑定多个 Condition 对象,实现更精细的线程等待/唤醒。
  • AQS(AbstractQueuedSynchronizer):

    • 这是 ReentrantLockCountDownLatchSemaphore 等同步器的核心框架。
    • 其内部维护了一个 volatile int state (代表资源状态)和一个 FIFO线程等待队列(CLH队列的变体)。
    • 核心方法是 acquire()release()。子类通过重写 tryAcquiretryRelease 方法来定义具体的资源获取和释放逻辑。

学习心得: 学习 ReentrantLock 绝不能止步于API调用,必须深入到AQS层面。理解了AQS,就等于拿到了Java并发工具包的"万能钥匙"。


第四部分:线程隔离的魔法 ------ ThreadLocal原理与陷阱

核心思想: 用空间换时间,为每个线程创建变量的副本,避免共享,从而实现线程安全。

  • 核心原理:

    • 每个 Thread 对象内部都有一个 ThreadLocalMap 类型的 threadLocals 变量。
    • ThreadLocalMapKey是ThreadLocal对象本身(弱引用),Value是存储的变量副本。
    • get()/set() 方法操作的是当前线程ThreadLocalMap,因此天然线程隔离。
  • 经典场景:

    • 用户会话信息(如User对象)传递,避免在方法间层层传递参数。
    • 数据库连接、事务管理(如Spring的 @Transactional)。
    • 日期格式化类 SimpleDateFormat 的线程安全包装。
  • 内存泄漏陷阱:

    • 根源: Key是弱引用,会在GC时被回收,但Value是强引用。如果线程长时间运行(如线程池中的线程)且不再使用该 ThreadLocal,就会导致Value无法被访问,却也无法被回收,造成内存泄漏。
    • 解决方案: 在使用完毕后,必须调用 threadLocal.remove() 方法,手动清除Entry。

学习心得: ThreadLocal 是解决特定场景并发问题的利器,但"能力越大,责任越大",务必记得及时清理,养成良好的编程习惯。


第五部分:容器的并发之道 ------ HashMap与ConcurrentHashMap

核心思想: 不同的并发场景下,需要选择不同并发级别的容器。

  • HashMap的并发死穴:

    • 在JDK7中,多线程并发扩容可能导致环形链表,引起CPU 100%。
    • 在JDK8中,虽然解决了死循环问题,但仍有数据覆盖、丢失等线程安全问题。结论:HashMap在任何情况下都不适用于多线程环境。
  • ConcurrentHashMap(JDK8+)的精妙设计:

    • 抛弃分段锁(JDK7): 采用 Node数组 + 链表 + 红黑树 结构,锁的粒度更细,直接锁住数组的每个桶(桶的头节点)。
    • 并发控制:
      • CAS + synchronized 初始化、插入头节点使用CAS无锁编程。对桶的头节点使用 synchronized 加锁,锁粒度小,性能极高。
      • sizeCtl等控制变量: 配合volatile读写和CAS,实现高效的并发扩容和大小统计。
    • 扩容: 支持多线程协助扩容,提升效率。

学习心得:Hashtable(全表锁)到 ConcurrentHashMap(分段锁)再到 ConcurrentHashMap(桶级别锁),这演进历程本身就是一部锁优化教科书。理解CHM,就能深刻体会如何在高性能和高并发之间找到平衡。


总结与展望

并发编程的学习是一个螺旋式上升的过程。从宏观的线程概念 出发,深入到CPU级别的缓存一致性(MESI) ,再上到Java语言层面的锁和同步工具(AQS) ,接着是线程隔离的ThreadLocal ,最后将这些知识融会贯通,理解顶级并发容器ConcurrentHashMap的设计哲学。

这条路径,不仅是知识的积累,更是思维方式的锤炼------从"会用"到"懂原理",再到"能优化"。希望这篇笔记能为你照亮前行的道路,共勉!


版权声明: 本文为个人学习笔记,部分原理总结自公开课程与技术文档,如有侵权请联系删除。欢迎交流,转载请注明出处。

相关推荐
无名之辈J3 小时前
GC Overhead 排查
后端
熊小猿4 小时前
如何在 Spring Boot 项目中使用 @Slf4j 注解结合 Logback 进行系统日志管理
java·开发语言·spring boot
倚栏听风雨4 小时前
jackson @JsonAnyGetter @JsonAnySetter 使用说明
后端
Mintopia4 小时前
🚀 Next.js 16 新特性深度解析:当框架开始思考人生
前端·后端·全栈
豐儀麟阁贵4 小时前
5.4静态变量和静态方法
java·开发语言
Good kid.4 小时前
一键部署 Deepseek网页聊天系统(基于 Spring Boot + HTML 的本地对话系统)
spring boot·后端·html
ytgytg284 小时前
芋道源码:VUE3部署:避坑--验证码不现显示,管理后台无法访问后端接口等,完善中。。。
java·vue
乾坤瞬间4 小时前
【Java后端进行ai coding实践系列】如何使用ai coding实现计划任务增删改查
java·人工智能·python