Java并发编程

线程生命周期、Java 内存模型 JMM、happens-before

20 篇文档MODULE 03
DOC

线程生命周期:BLOCKED 和 WAITING 常常是接续发生,不是二选一(031)

一、为什么要先搞清楚线程状态 进入并发编程这个新分类的第一篇,先不讲锁、不讲内存模型,而是从最基础的"一个线程一生会经历哪些状态"讲起——原因很简单: jstack 抓到的线程堆栈、线上死锁排查时看到的 BLO…

DOC

Java 内存模型 JMM:不是内存的物理布局,是一套规定谁能看见谁修改的规则(032)

一、为什么需要专门学一套"内存模型" 很多人第一次听到"Java 内存模型"(JMM,Java Memory Model)会下意识联想到堆、栈、方法区这些 JVM 运行时数据区——但这是两个完全不同的概念。运行…

DOC

happens-before:JMM 说要保证可见性,它说的是具体哪些操作之间才算保证(033)

一、为什么 JMM 之后还需要 happens before 上一篇讲到 Java 内存模型(JMM)定义了可见性、有序性、原子性这三个问题,也讲了 volatile 、 synchronized 是解决这些问…

DOC

volatile 原理:它不是轻量级锁,而是一份关于可见性和顺序的具体承诺(034)

一、为什么要把 volatile 单独拎出来讲 前两篇分别讲了 JMM 定义的可见性/有序性/原子性问题,以及 happens before 规则里"volatile 变量规则"这一条——对一个 volatil…

DOC

synchronized 原理:一把看起来很重的锁,其实内部藏着三级加速通道(035)

一、为什么要单独讲 synchronized 的底层实现 前几篇讲了 JMM、happens before、volatile,它们解决的都是可见性和有序性问题,唯独没有系统讲过"互斥"——也就是"同一时刻只允许…

DOC

CAS 与原子类:不用锁也能保证安全,但代价藏在你看不到的地方(036)

一、为什么会有 CAS:锁之外的另一条路 前几篇讲的 synchronized 走的是"互斥"这条路——同一时刻只让一个线程进入,其他线程排队甚至挂起。这条路很直观,但代价也很实在:线程挂起、唤醒要经过操作系统…

DOC

AQS 原理:一套造锁的框架,撑起了半个 java.util.concurrent(037)

一、为什么需要 AQS:不想每把锁都重新造轮子 前面几篇讲的 synchronized 是 JVM 内置的锁,CAS 是硬件提供的原子操作。但如果要在 Java 层面自己实现一把功能更丰富的锁——比如支持公平/…

DOC

ReentrantLock:公平锁、非公平锁、可中断锁与超时获取(038)

一、已经有 synchronized 了,为什么还需要 ReentrantLock 前一篇讲了 AQS 这套"造锁的框架", ReentrantLock 正是基于 AQS 独占模式实现的一把标准锁,可以理解成 …

DOC

ReentrantReadWriteLock 与 StampedLock:读多写少场景和乐观读(039)

一、普通互斥锁在读多写少场景下浪费了什么 前一篇讲的 ReentrantLock,本质上还是一把彻底的互斥锁——不管是读操作还是写操作,只要涉及这把锁保护的数据,同一时刻都只能有一个线程进入。但仔细想想很多实际…

DOC

wait/notify 与 Condition:等待通知模型、虚假唤醒和标准范式(040)

一、为什么不能直接"傻等" 假设一个线程要等某个条件满足才能继续干活——比如等一个缓冲区里有数据了才能取。最朴素的写法是写一个循环,不停地检查这个条件: 这种写法叫 忙等(busy wait) ,问题很明显:这…

DOC

ThreadLocal 原理:ThreadLocalMap、弱引用、内存泄漏与上下文传递(041)

一、为什么需要 ThreadLocal 两个典型场景能说明 ThreadLocal 存在的意义: 第一个场景是 线程不安全的工具类 。比如老版本的 SimpleDateFormat ,它内部有可变的成员变量,多…

DOC

线程池核心原理:核心线程、最大线程、工作队列、keepAliveTime(042)

一、为什么需要线程池 如果每次要执行一个异步任务,都直接 new Thread(task).start() ,会带来两个实际的问题。第一, 线程的创建和销毁本身是有成本的 ——操作系统要为每个线程分配独立的栈空…

DOC

线程池任务提交流程:execute 决策路径、Worker 机制与异常处理(043)

一、execute 方法:一次任务提交要经过哪几层判断 上一篇讲清楚了 corePoolSize 、 maximumPoolSize 、 workQueue 、 keepAliveTime 这几个参数各自控制什…

DOC

线程池参数估算:CPU 密集、I/O 密集、Little 定律与压测校准(044)

一、"线程数该设多少":一个没有标准答案、但有方法论的问题 前面几篇讲清楚了 corePoolSize 、 maximumPoolSize 、 workQueue 各自的行为,也讲清楚了 execute 内部的…

DOC

线程池队列与拒绝策略:有界队列、背压、降级和自定义策略(045)

一、为什么"队列选型"和"拒绝策略"经常被低估 前几篇把线程池的参数估算讲得很细: corePoolSize 该设多少、 maximumPoolSize 该设多少、甚至怎么用阻塞系数公式和 Little 定律去…

DOC

Future 与 CompletableFuture:异步编排、异常传播、线程池隔离(046)

一、从"提交任务却拿不到结果"这个痛点说起 前几篇讲的都是线程池内部的运转机制——参数怎么估算、队列怎么选、拒绝策略怎么设计。但这些讨论有一个共同的前提:任务提交进去之后,"怎么拿到任务的执行结果"这件事本身,…

DOC

ForkJoinPool 原理:工作窃取、任务拆分和 commonPool 风险(047)

一、为什么普通线程池不够用,得单独讲 ForkJoinPool 前一篇讲到 CompletableFuture 的 xxxAsync 方法如果不显式传入线程池,会默默用上 ForkJoinPool.common…

DOC

并发工具类:CountDownLatch、CyclicBarrier、Semaphore、Phaser(048)

一、锁解决的是"互斥",这几个工具解决的是"节奏" 前面几篇讲的 synchronized 、 ReentrantLock 、 AQS 解决的核心问题是"互斥"——同一时刻只让一个线程进入临界区,避免数据被并发…

DOC

死锁、活锁与饥饿:产生条件、检测工具、规避与恢复方案(049)

一、三个经常被混为一谈、但本质不同的问题 多线程编程里,"程序卡住不动了"是最让人头疼的故障现象之一,但"卡住"背后至少对应三种完全不同的机制: 死锁 (deadlock)、 活锁 (livelock)、 饥饿…

DOC

虚拟线程(Project Loom):调度模型、pinning、适用场景与平台线程对比(050)

一、为什么会有虚拟线程:平台线程撞到的天花板 Java 从 1.0 开始, Thread 就直接对应一个操作系统线程——这类线程在 Project Loom 之后被称为 平台线程 (Platform Threa…