JMM 是一种规范,是解决由于多线程通过共享内存进行通信时,存在的本地内存数据不一致、编译器会对代码指令重排序、处理器会对代码乱序执行等带来的问题。
目的是保证并发编程场景中的原子性、可见性和有序性。
下面我们就再来看下,在 Java 中,分别使用什么方式来保证。
原子性
在 Java 中,为了保证原子性,提供了两个高级的字节码指令 Monitorenter 和 Monitorexit。
在 Synchronized 的实现原理文章中,介绍过,这两个字节码,在 Java 中对应的关键字就是 Synchronized。
因此,在 Java 中可以使用 Synchronized 来保证方法和代码块内的操作是原子性的。
(volatile只能保证单个操作的原子性)
可见性
Java 内存模型是通过在变量修改后将新值同步回主内存,在变量读取前从主内存刷新变量值的这种依赖主内存作为传递媒介的方式来实现的。
Java 中的 Volatile 关键字提供了一个功能,那就是被其修饰的变量在被修改后可以立即同步到主内存。
被其修饰的变量在每次使用之前都从主内存刷新。因此,可以使用 Volatile 来保证多线程操作时变量的可见性。
除了 Volatile,Java 中的 Synchronized 和 Final 两个关键字也可以实现可见性。只不过实现方式不同,这里不再展开了。
有序性
在 Java 中,可以使用 Synchronized 和 Volatile 来保证多线程之间操作的有序性。
实现方式有所区别:Volatile 关键字会禁止指令重排。Synchronized 关键字保证同一时刻只允许一条线程操作。
(单线程中任何操作都是有序的,一个线程看另一个线程的操作都是无序的)
你可能发现了,好像 Synchronized 关键字是万能的,它可以同时满足以上三种特性,这也是很多人滥用 Synchronized 的原因。
但是 Synchronized 是比较影响性能的,虽然编译器提供了很多锁优化技术,但是也不建议过度使用。