zoukankan      html  css  js  c++  java
  • Java 内存模型(Java Memory Model,JMM)

    基本概念

    • JMM 本身是一种抽象的概念并不是真实存在,它描述的是一组规范,通过这组规范定义了程序的访问方式
    • JMM 同步规定
      • 线程解锁前,必须把共享变量的值刷新回主内存
      • 线程加锁前,必须读取主内存的最新值到自己的工作内存
      • 加锁解锁是同一把锁
    • 由于 JVM 运行程序的实体是线程,而每个线程创建时 JVM 都会为其创建一个工作内存,工作内存是每个线程的私有数据区域,而 Java 内存模型中规定所有变量储存在主内存,主内存是共享内存区域,所有的线程都可以访问,但线程对变量的操作(读取赋值等)必须都工作内存进行
    • 首先要将变量从主内存拷贝的自己的工作内存空间,然后对变量进行操作,操作完成后再将变量写回主内存,不能直接操作主内存中的变量,工作内存中存储着主内存中的变量副本拷贝,工作内存是每个线程的私有数据区域,因此不同的线程间无法访问对方的工作内存,线程间的通信(传值)必须通过主内存来完成

    (内存模型图)

    三大特性 

    原子性

    public class VolatileDemo {
        public static void main(String[] args) {
           test01();
        }
    
        // 测试原子性
        private static void test01() {
            Data data = new Data();
            for (int i = 0; i < 20; i++) {
                new Thread(() -> {
                    for (int j = 0; j < 1000; j++) {
                        data.addOne();
                    }
                }).start();
            }
            // 默认有 main 线程和 gc 线程
            while (Thread.activeCount() > 2) {
                Thread.yield();
            }
            // 发现不能输出 20000
            System.out.println(data.a);
        }
    }
    
    class Data {
        volatile int a = 0;
        void addOne() {
            this.a += 1;
        }
    }   

    可见性

    public class VolatileDemo {
        public static void main(String[] args) {
            Data data = new Data();
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + " coming...");
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                data.addOne();
                System.out.println(Thread.currentThread().getName() + " updated...");
            }).start();
    
            // 主线程会进入死循环,说明当有一个线程修改了值,默认不会马上被另一个线程感知到
            while (data.a == 0) {
                // looping
            }
            System.out.println(Thread.currentThread().getName() + " job is done...");
        }
    }
    
    class Data {
        // int a = 0;
        void addOne() {
            this.a += 1;
        }
    }

    有序性

      • 计算机在执行程序时,为了提高性能,编译器和处理器常常会对指令做重排,一般分为以下 3 种
        • 编译器优化的重排
        • 指令并行的重排
        • 内存系统的重排
      • 单线程环境里面确保程序最终执行的结果和代码执行的结果一致
      • 处理器在进行重排序时必须考虑指令之间的数据依赖性
      • 多线程环境中线程交替执行,由于编译器优化重排的存在,两个线程中使用的变量能否保证用的变量能否一致性是无法确定的,结果无法预测
    public class ReSortSeqDemo {
        int a = 0;
        boolean flag = false;
        
        public void method01() {
            a = 1;           // flag = true;
                             // ----线程切换----
            flag = true;     // a = 1;
        }
    
        public void method02() {
            if (flag) {
                a = a + 3;
                System.out.println("a = " + a);
            }
        }
    
        // 两个线程同时执行method01 和 method02, 如果线程 1 执行 method01 重排序了,然后切换的线程 2 执行 method02 就会出现不一样的结果
    
    }
  • 相关阅读:
    Educational Codeforces Round 20 D. Magazine Ad
    Educational Codeforces Round 20 C. Maximal GCD
    紫书第三章训练2 暴力集
    Educational Codeforces Round 20 B. Distances to Zero
    Educational Codeforces Round 20 A. Maximal Binary Matrix
    紫书第三章训练1 D
    紫书第一章训练1 D -Message Decoding
    HAZU校赛 Problem K: Deadline
    Mutual Training for Wannafly Union #8 D
    紫书第三章训练1 E
  • 原文地址:https://www.cnblogs.com/ding-dang/p/13141733.html
Copyright © 2011-2022 走看看