Java JMM 和可见性问题
企业开发
2020-03-17 10:17:18
阅读次数: 0
JMM 和可见性问题
1. JVM
JVM(Java Virtual Machine,即 Java 虚拟机)是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的;
Java 使用 JVM 屏蔽了与具体平台相关的信息,使其在不同平台上运行时不需要重新编译,只需生成在 JVM 上运行的目标代码(字节码),就可以在多种平台上不加修改地运行;
JVM 组成部分
说明
程序计数器(Program Counter Register)
是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器
Java虚拟机栈(Java Virtual Machine Stacks)
是 Java 方法执行的内存模型,每个方法在执行的同时都会创建一个线帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息
Java 堆(Java Heap)
是 Java 虚拟机中内存最大的一块,是被所有线程共享的,在虚拟机启动时候创建
本地方法栈(Native Method Stack)
与虚拟机栈的作用是一样的,只不过虚拟机栈是服务 Java 方法的,而本地方法栈是为虚拟机调用 Native 方法服务的
方法区(Methed Area)
用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译后的代码等数据
Java 堆(Java Heap)
为所有创建的对象和数组分配内存空间,被 JVM 中所有的线程共享
2. JMM
为了能够支持多线程程序,又设计了 JMM(Java Memory Model,即 Java 的内存模型),是 JVM 使用的一种内存模型,是 JVM 的另一种抽象表现形式,它将线程堆栈和堆之间的内存分开;
运行在 JVM 上的每个线程都有自己的线程堆栈(Thread Stack),一个线程只能访问它自己的线程堆栈,一个线程可能会将一个有限变量的副本传递给另一个线程,但它不能共享原始局部变量本身;
堆(Heap)包含在 Java 应用程序中创建的所有对象,并且可以被具有对象引用的所有线程访问,如果两个线程同时调用同一个对象上的一个方法,它们都可以访问该对象的成员变量,但每个线程都有自己的局部变量副本,堆中的数据是共享的,线程不安全的;
JMM 组成部分
说明
主存区(Main Memory 或 Java Heap Memory)
Java 中所有变量都是存在主存中的,对于所有线程进行共享
工作内存(Working Memory 或 Thread Stack)
工作内存中保存的是主存中某些变量的拷贝,线程对所有变量的操作都是发生在工作内存中,而线程之间的相互访问则是依赖主存来完成
存储关卡(Memory Barrier)
刷新缓存,或使缓存无效
3. 可见性问题
JMM 的两个关键概念:可见性(Visibility)和可排序性(Ordering),排序性问题出现概率极低,一般都是考虑可见性问题;
共享对象的可见性是指如果两个或多个线程共享一个对象,但没有正确使用 volatile 声明或 Synchronized 同步机制,那么当一个线程更新了共享变量值后,该过程对于其他线程来讲是不可见的,例如,线程 A、B 要同时进行修改变量 c,线程 A 和 B 在各自的 Thread Stack 中维护了一份局部变量 c 的副本,线程 A 修改了线程 A 中 Thread Stack 中的局部变量
c
A
c_A
c A ,但是还没有还没将修改的数据刷新到 Heap Memory 中,而线程 B 获取的值依然是 c 的旧值,就出现了可见性问题,详见:Java 高并发及线程安全 ;
可以使用 volatile 关键字的方式解决(乐观锁),详见:Java 关键字 volatile ;
使用 synchronized 同步机制(悲观锁),详见:Java synchronized 线程的同步 ;
其他方式,如 JDK 的并发包里提供的几个非常有用的并发容器和并发工具类,详见:Java 并发包 ;
发布了242 篇原创文章 ·
获赞 244 ·
访问量 9567
转载自 blog.csdn.net/Regino/article/details/104883073