java线程工作内存刷新时机

工作内存和主内存的同步

Posted by 高强 on March 13, 2020

JMM 模型

  • Java虚拟机规范中试图定义一种Java内存模型来屏蔽各种硬件和操作系统的内存访问差异,以实现让Java程序在各种平台下都能达到一致的内存访问效果
  • 规定所有的变量都存储在主内存中,线程使用到的变量的主内存副本拷贝保存在线程工作内存中
  • 线程对变量的所有操作都必须在工作内存中进行,不能直接读写主内存中的变量
  • 不同线程之间无法直接访问对方工作内存中的变量

示例

public class JMMTest {
    public static void main(String[] args) {
        ThreadDemo demo = new ThreadDemo();
        new Thread(demo, "ThreadDemo").start();
        while (true) {
            // 此处加入 println 或 synchronized 代码块
            // System.out.println();
            // synchronized (JMMTest.class) {}
            if (demo.flag) {
                System.out.println("主线程跳出循环");
                break;
            }
        }
    }
}

class ThreadDemo implements Runnable {

    public boolean flag = false;

    @Override
    public void run() {
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        flag = true;
        System.out.println("线程: " + Thread.currentThread().getName() + "执行完毕, 置 flag = " + flag);
    }
}

运行效果:

线程: ThreadDemo执行完毕, 置 flag = true

之后主线程一直阻塞。
分析:

  • 主线程中 while 循环中一直读取的是工作内存的 flag 值, 未感知到 ThreadDemo 线程对 flag 变量的改变
  • 在上方代码中加入 println(实现源码中包含 synchronized 代码块) 或 synchronized 代码块,可以达到预期效果,主线程正常跳出循环并结束运行
  • flag 变量用 volatile 修饰也能达到预期效果

《Concurrent Programming in Java》 表述

In essence, releasing a lock forces a flush of all writes from working memory employed by the thread, and acquiring a lock forces a (re)load of the values of accessible fields. While lock actions provide exclusion only for the operations performed within a synchronized method or block, these memory effects are defined to cover all fields used by the thread performing the action.

思考理解

  • 线程释放一个锁的时候将当前线程工作内存中发生的所有写操作刷新到主内存中
  • 线程获取一个锁的时候会对当前线程所有可访问到的值从主内存同步到工作内存中
  • 使用 volatile 修饰的变量,每次修改都会立即同步到主内存,每次读如果发现变量值有变更(依据CPU MESI等一致性协议),则直接从主内存中读取
  • 当一个线程终止时,所有写入的操作都被刷新到主内存中

资料参考


Related Issues not found

Please contact @peiqianggao to initialize the comment