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

哪些操作会清除线程本地缓存?解析Java示例中操作的共性

关于Java线程缓存与操作触发内存可见性的问题

示例代码与现象

参考的示例代码如下:

public class VolatileKeyword {
    private static final Logger LOGGER = Logger.getLogger(VolatileKeyword.class);
    private static int MY_INT = 0;

    public static void main(String[] args) {
        new ChangeListener().start();
        new ChangeMaker().start();
    }

    static class ChangeListener extends Thread {
        @Override public void run() {
            int local_value = MY_INT;
            while (local_value < 5) {
                // (A)
                if (local_value != MY_INT) {
                    LOGGER.info("Got Change for MY_INT : " + MY_INT);
                    local_value = MY_INT;
                }
            }
        }
    }

    static class ChangeMaker extends Thread {
        @Override public void run() {
            int local_value = MY_INT;
            while (MY_INT < 5) {
                LOGGER.log(Level.INFO, "Incrementing MY_INT to " + (local_value + 1));
                MY_INT = ++local_value;
                try {
                    Thread.sleep(500);
                }
                catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

原程序输出

原程序中ChangeListener线程只会读取本地缓存的MY_INT值,输出如下:

Incrementing MY_INT to 1
Incrementing MY_INT to 2
Incrementing MY_INT to 3
Incrementing MY_INT to 4
Incrementing MY_INT to 5

修改(A)处后的输出

若将// (A)替换为以下任一操作,输出会和MY_INT声明为volatile时一致:

  • Thread.currentThread().isAlive();
  • Thread.sleep(0)
  • Thread.interrupted();
  • System.out.print("");
  • 为ChangeListener添加volatile整数x,将// (A)替换为x += MY_INT

此时输出为:

Incrementing MY_INT to 1
Got Change for MY_INT : 1
Incrementing MY_INT to 2
Got Change for MY_INT : 2
Incrementing MY_INT to 3
Got Change for MY_INT : 3
Incrementing MY_INT to 4
Got Change for MY_INT : 4
Incrementing MY_INT to 5
Got Change for MY_INT : 5

问题

上述操作的共同特性是什么,导致线程缓存被清除?补充说明:最后两个操作明显涉及volatile或同步代码从而触发缓存重置,但其他操作的原因是什么?

解答

这些操作的核心共同点是:它们都会触发Java内存模型(JMM)中的内存屏障(Memory Barrier),强制线程刷新本地缓存,重新从主内存读取共享变量。具体拆解:

  • Thread.currentThread().isAlive()、Thread.sleep(0)、Thread.interrupted():这类Thread类的方法内部包含线程状态检查或底层同步逻辑,JVM会自动为其插入内存屏障,确保线程对共享变量的读写操作与主内存同步,打破本地缓存的只读优化。
  • System.out.print(""):System.out对应的PrintStream类,所有输出方法都带有synchronized同步块。进入/退出同步块时,JMM会强制线程将本地缓存的修改刷入主内存,同时从主内存重新读取所有共享变量的最新值,相当于隐式触发了内存可见性保证。
  • 涉及volatile变量的操作:volatile变量的读写本身就会触发内存屏障——写volatile会强制刷本地缓存到主内存,读volatile会强制从主内存读取,同时根据JMM的happen-before规则,volatile操作会带动周围共享变量的内存可见性,因此MY_INT的读取也会同步到主内存的最新值。

本质上,这些操作都间接触发了内存屏障,让ChangeListener线程能看到ChangeMaker线程修改的MY_INT最新值,行为就和MY_INT被声明为volatile一致。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 18:30:50