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

Java是否允许无界饥饿?volatile循环程序的JMM合规性问询

引言

考虑以下Java程序:

public class Loop {
    static volatile boolean flag;
    public static void main(String[] args) {
        new Thread(() -> flag = true).start();
        while(!flag);
    }
}

简单描述:存在两个线程,一个线程在循环中等待volatile boolean flag变为true,另一个线程将flag设为true。

我认为Java语言规范(JLS)并未保证该程序会终止,以下是我的推理过程。但由于类似程序有时被作为Java同步的正确示例,我不确定自己的理解是否正确。

推理过程

所有对JLS的引用均基于Java SE 23。为简洁起见,若未明确指定变量,提及的读/写操作均指对flag的读/写。

§17.4定义了Java内存模型(JMM),用于划分程序的合法与非法行为。§17.4.1至§17.4.7规定了合法执行的概念;§17.4.8进一步约束执行需满足因果性要求;§17.4.9基于合法执行定义了程序的可观测行为。

合法执行

首先,我认为对于任意n > 0,该程序存在一个包含恰好n次volatile读操作的合法执行,且无外部操作(除§17.4.9引入的executionTermination操作)。具体而言,该执行的同步操作按同步顺序排列如下:

  1. 两个线程A和B启动(即执行其合成初始操作;参见§17.4.2)。
  2. A执行n-1次volatile读操作,读取到默认值false的合成写入。
  3. B执行一次volatile写操作,将值设为true。
  4. A再执行一次volatile读操作,读取到B执行的volatile写操作的值。
  5. A和B均终止(即执行其合成最终操作;参见§17.4.2)。

我认为这些执行满足合法执行的所有要求及因果性条件,因数学论证繁琐在此省略。

可观测行为

接下来,我将论证仅包含hang操作的行为是该程序的可观测行为。

§17.4.9规定:

[...]
若O是执行E的可观测操作集,则O必须是E的操作集A的子集,且即使A包含无限个操作,O也只能包含有限个操作。此外,若操作y在O中,且存在hb(x, y)或so(x, y),则x也必须在O中。
[...]
行为B是程序P的允许行为当且仅当B是有限的外部操作集,且满足以下任一条件:

  • [...]
  • 存在操作集O,使得B由一个hang操作和O中的所有外部操作组成,且对于所有k ≥ |O|,存在P的执行E(操作集为A),以及操作集O',满足:
    • O和O'均为A的子集,且满足可观测操作集的要求。
    • O ⊆ O' ⊆ A
    • |O'| ≥ k
    • O' - O不包含任何外部操作

选择B为仅包含hang操作的单元素集,O为空集。对于任意给定的k,选择E为包含恰好k次读操作的执行,O'为A去掉executionTermination后的集合,可满足所有条件。

实际JVM的行为

我在x86_64机器上使用OpenJDK(Oracle build 23+37-2369)进行了实验。只要flag是volatile,无法让程序挂起;仅当flag为非volatile时,C2会将循环优化为无限循环。

技术问询

  • 我对JLS/JMM的理解是否正确,即上述程序允许不终止?若不正确,我的错误在哪里?
  • 若我的理解正确,是否存在特定环境组合(CPU架构、JDK版本、JVM参数等),使得上述程序或其flag为volatile的变体在实际JVM上不终止?

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 15:18:09