You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何Java多线程下long类型撕裂现象无法复现?求解答

关于Java long类型写入撕裂现象的复现问题

嘿,这个问题我碰到过好多次——不少开发者都知道JLS里提到long/double的非原子性写入,但真动手复现撕裂现象时却屡屡失败,完全不是你的问题,我来帮你拆解清楚~

先给你吃个定心丸:你没误解规范

JLS 17.7确实明确规定了:对于64位的long和double类型,Java虚拟机允许将单个写入操作拆分为两个独立的32位写入操作(分别处理高32位和低32位)。在多线程场景下,如果一个线程正在写入这两个32位部分,另一个线程恰好读取,就可能得到一个混合值——也就是一次写入的前半段和另一次写入的后半段组合出来的“撕裂值”,比如原本交替写入0x0000000000000000和0xFFFFFFFFFFFFFFFF,结果读到0x00000000FFFFFFFF这种中间态。

为什么复现这么难?

问题出在硬件和JVM实现的实际行为上,理论上的可能性被现实条件限制了:

  • 硬件原子性支持:现在主流的64位CPU(x86_64、ARM64等)本身对64位内存访问是原子性的,哪怕JVM允许拆分,实际执行时也会直接用硬件提供的原子操作,自然不会出现撕裂。只有在32位CPU或者不支持64位原子访问的老旧硬件上,才会触发JVM的拆分逻辑。
  • JVM优化:即使在理论上可以拆分的环境中,JVM也可能对非volatile的long/double写入做优化,直接用原子操作完成,进一步降低了撕裂的概率。另外,如果你的代码里给long变量加了volatile修饰,那JLS要求这种写入必须是原子性的,肯定复现不了撕裂。
  • 极小的时间窗口:哪怕所有条件都满足,要刚好卡到“写入线程刚写完32位,读取线程就执行读取”的时机,概率极低——就像你要在高速行驶的汽车上接住掉下来的硬币一样,不是不可能,但很难碰到。

怎么提高复现概率?

如果一定要复现真实的JVM撕裂行为,可以试试这些方法:

  1. 切换到32位JVM:32位JVM无法直接执行64位原子写入,必须拆成两个32位操作,这是最容易复现的场景。
  2. 增加线程压力:让读写线程疯狂循环执行,比如写入线程交替写入两个极端值,读取线程持续检查是否出现非预期值,通过大量的循环次数来提高碰到时间窗口的概率。
  3. 模拟撕裂行为:如果找不到32位环境,可以手动把long拆成两个int变量分别写入,模拟JVM的拆分逻辑,这样能快速看到撕裂效果(虽然这是模拟,不是真实JVM行为,但能验证你对撕裂现象的理解)。

一个参考的复现代码示例

下面是一段可能复现撕裂的代码,建议在32位JVM上运行:

public class LongTearDemo {
    // 注意:不要加volatile,否则写入会变成原子操作
    private static long sharedValue = 0L;

    public static void main(String[] args) throws InterruptedException {
        // 写入线程:交替写入全0和全1的long值
        Thread writer = new Thread(() -> {
            while (!Thread.currentThread().isInterrupted()) {
                sharedValue = 0x0000000000000000L;
                sharedValue = 0xFFFFFFFFFFFFFFFFL;
            }
        });

        // 读取线程:检查是否出现既不是全0也不是全1的撕裂值
        Thread reader = new Thread(() -> {
            while (!Thread.currentThread().isInterrupted()) {
                long current = sharedValue;
                if (current != 0x0000000000000000L && current != 0xFFFFFFFFFFFFFFFFL) {
                    System.out.println("捕获到撕裂值:0x" + Long.toHexString(current));
                    // 找到结果后终止所有线程
                    writer.interrupt();
                    Thread.currentThread().interrupt();
                }
            }
        });

        writer.start();
        reader.start();
        writer.join();
        reader.join();
    }
}

在32位JVM上运行这段代码,大概率几分钟内就能捕获到撕裂值;但在64位JVM上,可能跑几个小时都出不来结果——这就是硬件原子性带来的差异。

内容的提问来源于stack exchange,提问作者kasterma

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 07:31:15