Java JMM 和可见性问题


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 的另一种抽象表现形式,它将线程堆栈和堆之间的内存分开;

JMM

  • 运行在 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 ,但是还没有还没将修改的数据刷新到 Heap Memory 中,而线程 B 获取的值依然是 c 的旧值,就出现了可见性问题,详见:Java 高并发及线程安全
  • 可以使用 volatile 关键字的方式解决(乐观锁),详见:Java 关键字 volatile
  • 使用 synchronized 同步机制(悲观锁),详见:Java synchronized 线程的同步
  • 其他方式,如 JDK 的并发包里提供的几个非常有用的并发容器和并发工具类,详见:Java 并发包
发布了242 篇原创文章 · 获赞 244 · 访问量 9567

猜你喜欢

转载自blog.csdn.net/Regino/article/details/104883073