在现在计算机的硬件构架中,每个处理器都有自己的高速缓冲存储器,用来解决处理器的运算速度和内存之前几个数理级的差距。而各个高速缓存与主内存之间的数据一致性要求则是通过缓存一致性协议来保证的。而Java虚拟机也有自己的内存模型,并且与硬件的缓存操作具有很高的可比性:
1.Java内存模型
规定了所有的变量都存储在主内存(Main Memory)中(与物理硬件的命名一致,可以类比,但此处仅是JVM内存的一部分),每个线程还有自己的工作内存(Working Memory,可与高速缓存类比),线程的工作内存中保存了被该线程使用到的变量的主内存副本拷贝(并非完全拷贝,具体的实现有待查证...),线程对变量的所有操作(读取,赋值等)都必须在工作内存中进行,而不能直接读写主内存中的变量(volatile同样如此)。
不同线程无法访问其它工作内存中的变量,线程间的变量的交互均需要通过主内存来完成。如上图。
1.1.内存间的交互操作
Java内存模型定义了以下8种操作来完成主内存和工作内存之间的交互:
lock(锁定):作用于主内存的变量,把一个变量标识为一条线程独占的状态。
unlock(解锁):作用于主内存的变量,把一个处于锁定状态的变量释放出来,释放后的变量才可以被其它线程锁定。
read(读取):作用于主内存的变量,把一个变量的值从主内存传输到线程的工作内存中,以便随后的load动作使用。
load(载入):作用于工作内存的变量,把read操作从主内存中得到的变量值放入工作内存的变量副本中。
use(使用):作用于工作内存的变量,把工作内存中一个变量的值传递给执行引擎,每当虚拟机遇到一个需要使用到变量的值的字节码指令时将会执行这个操作。
assign(赋值):作用于工作内存的变量,把一个从执行引擎接收到的值赋给工作内存的变量,每当虚拟机遇到一个给变量的赋值的字节码指令时执行这个操作。
store(存储):作用于工作内存的变量,把工作内存中的一个变量的值传送到主内存中,以便随后的write操作作使用。
write(写入):作用于主内存的变量,把store操作从工作内存中得到的变量的值放入主内存的变量中。
1.2.对于volatile型变量的特殊规则
volatile有两个语义,其一是保证此变量对所有线程的可见性:对volatile变量的写操作会立刻反应到主内存中,读操作始终会读取主内存中的当前最新值。这里使用当前是因为,Java里的面运算并非原子操作,这会导致volatile变量的运算在并发下是不安全的。下面的代码来自[深入理解JVM虚拟机]的12-1:
/** * volatile变量自增运算测试 * * @author zzm */ public class VolatileTest { public static volatile int race = 0; public static void increase() { race++; } private static final int THREADS_COUNT = 20; public static void main(String[] args) { Thread[] threads = new Thread[THREADS_COUNT]; for (int i = 0; i < THREADS_COUNT; i++) { threads[i] = new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 10000; i++) { increase(); } } }); threads[i].start(); } // 等待所有累加线程都结束 while (Thread.activeCount() > 1) Thread.yield(); System.out.println(race); } }如果这端代码正确并发的吗,结果应该是200000,但经过多次运行可以看到,输出的结果小于200000。问题出现在自增“race++”上,自增不是原子操作,可以看到class文件中increase()是由此来4条字节码指令构成的(不算return)。从字节码层面上可以分析出并发失败的原因:当getstatic指令把race的值取到操作栈顶时,volatile关键字保证了race的值此时是正确的,但当执行iconst_1、iadd这些指令时,其它线程可能已经把race的值加大了,此时操作栈顶的值就成了过期的数据,所以putstatic指令执行后就有可能把过期的数据写入主面存中。
public static void increase(); 0 getstatic chapter12.VolatileTest.race : int [13] 3 iconst_1 4 iadd 5 putstatic chapter12.VolatileTest.race : int [13] 8 return Line numbers: [pc: 0, line: 11] [pc: 8, line: 12]
volatile在下面两条规则以外,仍需通过加锁来保证原子性:
①运算结果不依赖于变量的当前值或能够确保只有单一的线程修该变量的值。
②变量不需要与其它的状态变量共同参与不变约束。
其二是禁止指令重排序优化,保证变量赋值操作的顺序与程序代码中的执行顺序一到。具体请参照[单例模式]②双重检查加锁(不推荐)的说明。
1.3.先行发生原则(happens-before)
先行发生原则是Java内存模型中定义的两项操作之间的顺序关系,如果操作A先行发生于操作B,那么操作A产生的影响是能被操作B观察到的,“影响”包括修该了内存中共享变量的值,发送了消息,调用了方法等。它是判断数据是否存在竞争、线程是否安全的主要依据。
下面是Java内存模型下一些"天然的"先行发生关系,无须任何同步已经存在:
程序次序规则(Program Order Rule):在一个线程内,按照程序执行顺序,书写在前面的操作先行发生于书写在后面的操作(控制流顺序而不是程序代码顺序)。
线程锁定规则(Monitor Lock Rule):一个unlock操作先行发生于后面对同一个锁的lock操作(同一个锁,时间上的先后)。
volatile变量规则(Volatile Variable Rule):对一个volatile变量的写操作先行发生于后面对这个变量的读操作(时间上的先后)。
线程启动规则(Thread Start Rule):Thread对象的start()方法先行发生于此线程的每一个动作。
线程终止规则(Thread Termination Rule):线程中的所有操作都先行发生于对此线程的终此检测(可用Thread.isAlive()的返回值等检测线程是否已经终止)。
线程中断规则(Thread Interruption Rule):对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生(Thread.interrupted()方法检测)。
对象终结规则(Finalizer Rule):一个对象的初始化(构造函数执行结束)完成先行发生于它的finalize()方法的开始
传递性(Transitivity):如果操作A先行发生于操作B,操作B先行发生于操作C,那么操作A先行发生于操作C。
相关推荐
通过阅读《深入理解Java虚拟机》第三章,开发者不仅可以深入理解Java的安全机制,还能学习到如何在实际项目中实施这些安全策略,提升应用的安全性。对于想要从事Java开发或者对系统安全有深入了解需求的人来说,这一...
Java 虚拟机(JVM)自动内存管理机制 Java 虚拟机(JVM)自动内存管理机制是 Java 语言的一大特色,它使得 Java 程序员无需手动管理内存,从而提高了开发效率和程序稳定性。JVM 自动内存管理机制主要通过 JVM 的...
总的来说,"深入理解Java虚拟机读书笔记之:第3章 安全(2)"主要涵盖了Java安全体系的核心概念,包括类加载器、权限模型、安全管理器以及相关工具的使用。理解这些内容对于任何希望构建安全、可靠的Java应用程序的...
### 学习深入理解Java虚拟机的前几章笔记 #### JVM内存模型 Java虚拟机(JVM)的内存模型主要分为两大类:线程共享区和线程私有区。 ##### 线程共享区 - **堆**:是所有线程共享的内存区域,在这里存放着对象实例...
深入理解 Java 虚拟机笔记 Java 虚拟机(JVM)是 Java 语言的运行环境,它负责解释和执行 Java 字节码。下面是 Java 虚拟机相关的知识点: 虚拟机内存结构 Java 虚拟机的内存结构主要包括以下几个部分: * 方法...
深入理解Java虚拟机,首先我们要明白Java虚拟机(JVM)的核心功能:它负责装载类文件,执行字节码,并管理内存。Java虚拟机的结构复杂且高效,主要由类装载器、执行引擎、内存管理和类库等组件构成。 类装载器是JVM...
《JVM:深入理解Java虚拟机》是一本深入解析Java虚拟机工作原理和技术细节的经典书籍。这份学习笔记将涵盖JVM的关键概念、架构以及它如何影响Java程序的性能。我们将探讨以下几个方面: 1. **JVM概述** Java虚拟机...
### 深入理解Java虚拟机(JVM)的关键知识点 #### 一、Java与Java虚拟机的关系 Java语言的设计者们为了使Java程序能够跨平台运行,引入了一个概念——Java虚拟机(JVM)。简单来说,Java源代码在编译成`.class`...
它的设计目标是实现“一次编写,到处运行”,通过Java虚拟机(JVM)确保代码在不同操作系统上都能运行。Java语言的特点包括简洁性、面向对象、健壮性、安全性、高效性和可移植性。 【基本语法】 Java的基本语法包括...
除了上述提到的基础知识点外,《深入理解Java虚拟机——JVM高级特性与最佳实践(第2版)》这本书籍还深入探讨了JVM的性能调优、并发编程、以及各种高级特性的具体应用。比如,对于性能调优,书中讲解了如何根据不同的...
《深入理解Java虚拟机》是Java开发者们深入探讨Java运行机制的经典之作,作者周志明以其深入浅出的讲解方式,揭示了Java虚拟机(JVM)的工作原理。本资源包含该书第三版的源码分析及学习笔记,旨在帮助读者更透彻地...
### 深入Java虚拟机JVM类加载学习笔记 #### 一、Classloader机制解析 在Java虚拟机(JVM)中,类加载器(ClassLoader)是负责将类的`.class`文件加载到内存中的重要组件。理解类加载器的工作原理对于深入掌握JVM以及...
本资源是关于OpenJDK7源码的分析和学习资料,旨在帮助开发者深入理解Java虚拟机的工作原理。 首先,我们来了解一下Java虚拟机的主要组件和功能: 1. **类加载器**:负责加载Java字节码文件(.class),将其转换为...
Java虚拟机(JVM)是Java程序运行的核心,它是一个抽象的计算机系统,负责执行Java字节码。在深入理解JVM之前,我们先要明白什么是字节码:Java源代码经过编译后生成的中间表示,即.class文件,里面包含的就是字节码...
8. **JVM原理**:理解JVM(Java虚拟机)的工作原理有助于优化程序性能。笔记会涉及类加载机制、内存模型、垃圾回收等JVM核心概念。 9. **设计模式**:笔记会介绍常见的23种设计模式,如单例模式、工厂模式、装饰器...
12. **JVM内存模型**:理解Java虚拟机的工作原理,包括堆内存、栈内存、方法区等,以及垃圾回收机制。 13. **Spring框架**:如果笔记涵盖了Spring,那么会讲解依赖注入、AOP、MVC等核心概念,这是Java开发中广泛...
- **Java程序运行**: Java程序需要在Java虚拟机(JVM)上运行。这意味着只要系统安装了合适的JVM,Java程序就可以在这个平台上运行。 #### 开发流程简述 1. **编写源文件**:使用`.java`扩展名保存源代码。 2. **...
张龙老师的笔记以实战和案例为导向,旨在帮助读者深入理解这些核心概念,并能熟练应用于实际项目中。通过学习这些内容,开发者不仅可以掌握Java编程基础,还能提升问题解决能力,为后续的Java Web、框架学习乃至...