java 内存模型的核心是围绕着在并发过程当中如何处理原子性、可见性、有序性这3个特性来展开的,它们是多线程编程的核心。java
<!-- more -->编程
先行发生是 Java 内存模型中定义的两个操做之间的偏序关系,这些先行关系无需任何同步器的协助就已经存在,能够在编码中直接使用。Java 内存模型对这些关系做了以下规则:缓存
volatile 修饰的变量保证了不一样线程对这个变量进行操做时的可见性,即一个线程修改了某个变量的值,这新值对其余线程来讲是当即可见的。由于当对普通变量进行读写的时候,每一个线程先从内存拷贝变量到CPU缓存中。若是计算机有多个CPU,每一个线程可能在不一样的CPU上被处理,这意味着每一个线程能够拷贝到不一样的CPU cache中。而volatile修饰的变量,JVM保证了每次读变量都从内存中读,跳过CPU cache这一步。volatile修饰的变量禁止进行指令重排序,因此能在必定程度上保证有序性。只能保证该变量所在的语句仍是原来的位置,并不能保证该语句以前或以后的语句是否被打乱。多线程
package com.pdh.test; /** * volatile 复合操做测试 * * @author pengdh * @date 2017/11/12 */ public class VolatileDemo { // 申明 volatile 变量 private static volatile int i = 0; // 计数 private static final int COUNT = 10; /** * 对 volatile 变量复合运算 */ private static void increase() { i++; } public static void main(String[] args) { // 启动 10 个线程分别对 i 进行 10000 次计算,正常状况结果为 100000 for (int j = 0; j < COUNT; j++) { new Thread(() -> { for (int k = 0; k < 10000; k++) { increase(); } }).start(); } // 等待全部累加线程所有执行结束,这里不一样 ide 中线程存活数不同, // 该示例代码在 idea 中运行,会多出一个 Monitor Ctrl-Break 线程,故条件是 > 2, // 若是在 Eclipse 中条件应为 > 1 while (Thread.activeCount() > 2) { Thread.yield(); } System.out.println(i); } }
如上代码正常运行结果应该打印100000,但实际结果基本得不到正确结果。这说明了 volatile 变量的复合运算并不具备原子性,想要获得正确结果,须要对 volatile 变量运算操做加锁或者加上同步块。并发
package com.pdh.test; /** * volatile 复合操做测试 * * @author pengdh * @date 2017/11/12 */ public class VolatileDemo { // 申明 volatile 变量 private static volatile int i = 0; // 计数 private static final int COUNT = 10; /** * 对 volatile 变量复合运算,使用 synchronized 同步 */ private static synchronized void increase() { i++; } public static void main(String[] args) { // 启动 10 个线程分别对 i 进行 10000 次计算,正常状况结果为 100000 for (int j = 0; j < COUNT; j++) { new Thread(() -> { for (int k = 0; k < 10000; k++) { increase(); } }).start(); } // 等待全部累加线程所有执行结束,这里不一样 ide 中线程存活数不同, // 该示例代码在 idea 中运行,会多出一个 Monitor Ctrl-Break 线程,故条件是 > 2, // 若是在 Eclipse 中条件应为 > 1 while (Thread.activeCount() > 2) { Thread.yield(); } System.out.println(i); } }
volatile适用于不须要保证原子性,但却须要保证可见性的场景。一种典型的使用场景是用它修饰用于中止线程的状态标记,如:app
package com.pdh.test; /** * volatile 复合操做测试 * * @author pengdh * @date 2017/11/12 */ public class VolatileDemo { // 申明 volatile 变量 private volatile boolean flag = false; // 计数 private static final int COUNT = 10; /** * 使用 volatile 变量做为线程结束标志 */ private void start() { new Thread(() -> { while (!flag) { System.out.println("Thread is running"); } }).start(); } private void shutdown() { flag = true; System.out.println("Thread is stop"); } public static void main(String[] args) throws InterruptedException { VolatileDemo demo = new VolatileDemo(); demo.start(); Thread.sleep(2000); demo.shutdown(); } }
在只需保证可见性的状况下,volatile 的同步机制性能要优于锁。ide
欢迎扫一扫关注 程序猿pdh 公众号!性能