引言
在我们日常项目开发工作中,对于并发场景中经常出现的神奇的Bug
大都是由于并发编程中的三个核心问题的不深入理解导致的,即如何进行高效的线程任务分配,如何有效的进行线程之间的相互协作以及如何保证在同一时刻只有一个线程进行共享变量的访问。那我们该如何解决并发编程这三座大山呢,这就需要我们对于Java
的内存模型有着深入的理解。本文主要从以下几个方面来阐述Java
的内存模型,希望对于学习并发编程的童鞋有所裨益。
- 并发编程三大特性说明
- volatile关键字说明
- Java内存模型
- 总结
一、并发编程三大特性说明
1、原子性
所谓原子性操作,就是类似数据库的事务操作,对应的一个操作或者多个操作,要么全部完成,要么全都失败。所有的操作不会被外界因素打断。
2、可见性
所谓可见性特性,就是当多个线程访问同一个变量时,如果一个线程修改了该值。其他线程也可以立马看到被修改后的变量的值。Java
中volatile
关键字保证了变量的可见性。当变量被volatile
关键字修饰后,只要有线程修改了该变量值时,修改后的值会被立即刷新到主存中。其他线程进行共享变量读取时,会从主存中进行数据读取。如下图所示:
3、有序性
所谓有序性,是指在操作系统中程序的执行顺序和编写的代码的顺序是一致的。但是在Java
内存模型中,为了提升运行效率是允许编译器和处理器对指令进行重排序的,但是不影响单线程运行的最终结果。
二、volatile关键字说明
volatile
关键字可以保证多线程环境下的变量可见性,但是无法保证多线程操作的原子性。当volatile
关键字修饰共享变量进行写操作时,在其对应的汇编代码中会存在以Lock
为前缀的指令信息,改指令会促使处理器将缓存回写到内存中,所以说该变量具有以下两种特性:
(1)变量具备可见性特征,当一个线程对其进行修改后,其他线程可以读到期修改后的值,也就是说它会告诉编译器,对修饰变量的读写不实用CPU
的缓存,而是在主存中进行读取和写入;
(2)禁止进行指令重排序;
所谓指令重排序,就是计算机为了提高系统的执行效率,会对计算机指令进行重排序的操作:
a、编译器重排序,即编译器在不改变单线程的程序予以的情况下,重新编排程序语句的执行顺序;
b、处理器重排序,如果程序代码中对于数据的前后依赖不敏感,处理器可以改变程序语句对应的机器码的执行顺序。
volatile
关键字相对于synchronized
关键字会显得轻一点,在某些场合可以替代synchronized
关键字。
三、Java内存模型
我们知道在Java
技术体系中,它是采用内存共享的模式来实现不同线程之间的信息通信。Java
内存模型与硬件内存架构存在一定的映射关系
happens-before
是JMM
中非常重要的一个原则,通过它来判断数据是否存在竞争、线程是否安全的重要依据。那到底什么是happens-before
原则呢,网上的解释各有各的说法,按照自己的理解就是前面的一个操作的结果对于后续的操作来说是可见的。
四、总结
Java
内存模型是我么理解并发编程的关键,也是编发编程的内功。本文通过三大特性的说明、volatile
关键字的简单介绍以及JMM和物理架构的映射来简单阐述了Java
内存模型。