这里是全站文章的归档入口,下面是按时间倒序排列的全部文章。若想按主题浏览,请从首页的专题分类进入。
《Java 并发编程·线程与内存模型》7.3 ScopedValue:JDK 25 的作用域值
ScopedValue 用词法作用域取代线程绑定:绑定不可变、出了 run/call 自动解绑、结构化子任务自动继承、普通线程不继承。本机实测 JDK 25 上它已转正无需 preview,而 StructuredTaskScope 仍需 --enable-preview,附完整编译与运行输出。
posts
这里是全站文章的归档入口,下面是按时间倒序排列的全部文章。若想按主题浏览,请从首页的专题分类进入。
ScopedValue 用词法作用域取代线程绑定:绑定不可变、出了 run/call 自动解绑、结构化子任务自动继承、普通线程不继承。本机实测 JDK 25 上它已转正无需 preview,而 StructuredTaskScope 仍需 --enable-preview,附完整编译与运行输出。
InheritableThreadLocal 只在线程创建时复制一次快照,这个前提在线程池里不成立。实测同一个池线程连续服务三个请求,读到的都是第一个请求的会员号;父线程在创建后改值,子线程仍看到旧值。拆解串值、丢失、残留、半更新四种失败形态,给出方法参数、提交侧捕获、Executor 装饰器三种显式透传写法。
ThreadLocal 的哈希表、弱引用 key 与强引用 value 如何组合成一条泄漏路径。用反射读出 ThreadLocalMap 的 table 与 Entry 实测:6 个已失效的 ThreadLocal 仍留下 24 MiB 值,8 线程池不 remove 保留 32 MiB,remove 后为 0。
用订单库存扣减场景实现确定性迷你 Raft:逻辑 tick 驱动消息队列、按节点 ID 与任期错开选举超时、AppendEntries 冲突回退、多数派且当前任期才提交;分区测试验证少数派既选不出新主,也提交不了任何条目,并给出超时参数的取舍表。
从「回声」用户被二十条连赞推送逼到关闭通知权限的事故写起,拆解自己操作、被屏蔽者、短时间刷屏和聚合窗口四类触发边界,说明逐条通知与聚合通知的取舍,给出 ShouldNotify 的 Go 实现、时间窗口与维度隔离测试,以及真实运行输出和常见错误清单。
shutdown hook 是 JVM 退出前的最后一程,不是优雅停机的全部。本文用 50 次运行证明多个 hook 的启动顺序完全随机,实测异常不改变退出码、JVM 对 hook 没有超时、hook 里调用 System.exit 会卡死,并对比 SIGTERM、SIGKILL 与 Runtime.halt 的路径,最后给出 libraryd-conc 的单 hook 有序释放写法。
两阶段终止只有一个骨架:volatile 停止标志、interrupt 唤醒阻塞、join 限时等待。本文给出可直接使用的 Worker 完整类,在本机对比只发信号、立即中断、超时升级三条关停路径,并用真实输出说明 Future.cancel(true) 只是请求:任务不检查中断时,cancel 返回 true 也停不下来。
interrupt 不是杀死线程,只是把一个标志位置为 true,真正的退出仍要靠被中断线程自己检查。本文用 sleep、wait、join、synchronized 竞争与 ReentrantLock 在本机实测中断行为,说明 InterruptedException 为什么会清除标志、哪些阻塞根本不响应中断,并给出捕获后恢复标志再包装成 LoanException 的写法与边界。
从单锁 notifyAll 版写到 ReentrantLock 双条件版,补上关闭语义与中断处理,再和 ArrayBlockingQueue 正面对比。三个实现各跑 10 万元素全部有序正确,单锁版连续四次都在 39ms 以上,双条件版与 JDK 队列则贴着 20ms 上下波动。
wait 可能无缘无故返回,也可能醒来发现条件已被别人抢走,还可能信号早于等待而永久丢失。用两组实测对照说明:同一个双消费者场景里 if 版 100 轮全部取到 null,while 版 0 轮出错;而信号先发时裸 wait 等了 205ms 才超时。
wait 会释放监视器,notify 不会——两组实测时间线把这件事讲清楚,也说明不在同步块内调用为什么会抛 IllegalMonitorStateException。再给一个能跑的生产者消费者最小实现,讲清条件判断为什么必须写成 while、notify 与 notifyAll 该怎么选、被唤醒之后还要重新抢锁。
JIT 对 synchronized 做了哪些手脚:逃逸分析如何让方法内的锁消失、锁粗化如何合并循环里的加锁、偏向锁为什么在 JDK 25 已彻底移除。附 PrintFlagsFinal、UseBiasedLocking 报错与 LockingMode 弃用的本机真实输出。
用 javap 拆开 synchronized:同步方法靠 ACC_SYNCHRONIZED 标志,同步块生成 monitorenter/monitorexit 与异常表;再讲对象头 Mark Word 的锁状态、可重入计数,以及锁错对象、锁字符串常量这几类真实错误。
用 javap 与实测把 volatile 的三重语义拆开:它保证可见性与有序性,却不保证复合原子性;同步给出 DCL 的正确写法、8 线程下 volatile 计数器仍然丢更新的真实数据,以及 volatile 数组、volatile 引用与字段内容之间的边界。
解释 final 字段的初始化安全保证及其前提,用 ThisEscape 反例说明构造逸出如何让 final 失效;给出静态初始化、final 字段、volatile 引用、并发容器四类安全发布方式,并划清「引用可见」与「对象内部不可变」的边界。
从「回声」一条违规评论在正文审核通过后存活四十分钟的事故写起,拆解正文、根评论、回复和昵称各自的审核时机,说明折叠为什么不能等于删除、根评论为什么不应被折叠,给出 Fold 规则的 Go 实现、拉黑子树与阈值测试,以及真实运行输出和常见错误清单。
把 JMM 的八条 happens-before 规则逐条拆成最小代码,说明它们只约束可见性而非实际执行顺序;再用规则链推导双重检查锁定为什么必须加 volatile,给出四道「看到 A 是否一定看到 B」的推导练习与答案,并在借阅服务里用 volatile 发布不可变审计快照。
从一段停不下来的循环入手,拆开重排序、缓存可见性与有序性三个层次,说明 as-if-serial 只对单线程负责;给出无 volatile 与有 volatile 的停机标志完整程序及本机实跑输出,并说明这类 bug 为何依赖 JIT、CPU 与运行时长方能显现。
用 JCStress 0.16 把计数器竞态变成可重复的统计结果:完整给出 LostUpdate 与 AtomicCounter 两个测试类,解释五个注解、ACCEPTABLE/FORBIDDEN 与结果表读法,并贴出 Apple M1 Pro 上两次 quick 模式的真实输出,说明 FORBIDDEN 触发 AssertionError 是预期行为。
把同一个有竞态的借阅服务分别用不可变对象、synchronized、ThreadLocal 和 ConcurrentHashMap 改写,逐项比较代价与失效场景,并给出一张按共享可变性、状态大小和读写比选型的矩阵,说明并发容器的复合操作为何仍然不安全,最后给出单机、多实例与跨 key 场景下的取舍。