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 公衆號!性能