由於JVM的最佳化,多個執行緒之間的記憶體是彼此不可見的;可使用volatile修飾,JVM的最佳化中取消了指令重排序;
指令重排序:
JVM中,為了儘可能減少記憶體操作速度遠慢於CPU執行速度所帶來的CPU空置的影響,虛擬機器會按照自己的一些規則將程式編寫順序打亂,即寫在後面的程式碼在時間順序上可能會先執行,而寫在前面的程式碼會後執行——以儘可能充分地利用CPU。
Java提供了兩個關鍵字:volatile和synchronized來保證多執行緒之間操作的有序性,volatile關鍵字本身透過加入記憶體屏障來禁止指令的重排序,而synchronized關鍵字透過一個變數在同一時間只允許有一個執行緒對其進行加鎖的規則來實現。
在單執行緒程式中,不會發生“指令重排”和“工作記憶體和主記憶體同步延遲”現象,只在多執行緒程式中出現。
內容可見性問題,程式碼:
package com.what21.juc.visibility.case01.demo01;public class ThreadVisbilityDemo { public static void main(String[] args) { MyRunnable myRunnable = new MyRunnable(); new Thread(myRunnable).start(); while (true) { if (myRunnable.isFlag()) { System.out.println("......................"); break; } } System.out.println("程式結束!"); // 效果:是5秒之後列印,然後程式結束 // 實際:程式停止不下來 } public static class MyRunnable implements Runnable { private boolean flag = false; public boolean isFlag() { return flag; } @Override public void run() { try { Thread.sleep(5 * 1000); } catch (InterruptedException e) { e.printStackTrace(); } flag = true; } }}
實現效果:是5秒之後列印,然後程式結束,實際:程式停止不下來。
使用synchronized來解決,程式碼:
package com.what21.juc.visibility.case01.demo01;public class ThreadVisbilityDemo2 { public static void main(String[] args) { MyRunnable myRunnable = new MyRunnable(); new Thread(myRunnable).start(); while (true) { // 加鎖處理 synchronized (myRunnable) { if (myRunnable.isFlag()) { System.out.println("......................"); break; } } } System.out.println("程式結束!"); } public static class MyRunnable implements Runnable { private boolean flag = false; public boolean isFlag() { return flag; } @Override public void run() { try { Thread.sleep(5 * 1000); } catch (InterruptedException e) { e.printStackTrace(); } flag = true; } }}
使用volatile來解決,程式碼:
package com.what21.juc.visibility.case01.demo01;public class ThreadVisbilityDemo3 { public static void main(String[] args) { MyRunnable myRunnable = new MyRunnable(); new Thread(myRunnable).start(); while (true) { // 加鎖處理 if (myRunnable.isFlag()) { System.out.println("......................"); break; } } System.out.println("程式結束!"); } public static class MyRunnable implements Runnable { // 關鍵詞volatile private volatile boolean flag = false; public boolean isFlag() { return flag; } @Override public void run() { try { Thread.sleep(5 * 1000); } catch (InterruptedException e) { e.printStackTrace(); } flag = true; } }}
指令重排序,分類說明:
1、編譯器最佳化的重排序,編譯器在不改變單執行緒程式語義的前提下,可以重新安排語句的執行順序。
2、指令級並行的重排序,現代處理器採用了指令級並行技術(Instruction-LevelParallelism,ILP)來將多條指令重疊執行。如果不存在資料依賴性,處理器可以改變語句對應機器指令的執行順序。
3、記憶體系統的重排序,由於處理器使用快取和讀/寫緩衝區,這使得載入和儲存操作看上去可能是在亂序執行。