為了更清楚的了解可見性,這里先介紹一下jmm。jmm並非是一個組件或者程序,他是抽象計算機內存模型的一組規則、規范,定義了變量訪問內存的方式。
如圖:
jmm的每個線程有獨立的工作內存,他們的工作方式是從主內存將變量讀取到自己的工作內存,然后在工作內存中進行邏輯或者自述運算再把變量寫回到主內存中。正常情況下各線程的工作內存之間是相互隔離的、不可見的。
有這樣一個場景,兩個小孩子玩游戲,游戲內容是A小孩跳繩,B小孩叫停。
代碼示例1:
public class Game {
private volatile boolean stop = false;
public void stop() {
this.stop = true;
String name = Thread.currentThread().getName();
System.out.println(name + "小孩叫停: ");
}
public void ropeSkipping() {
String name = Thread.currentThread().getName();
int i = 0;
for (;!this.stop;i++) {
}
System.out.println(name + "小孩跳了: " + i + "次");
}
}
public class Play {
public static void main(String[] args) {
Game game = new Game();
Thread childB = new Thread(()->{
game.stop();
},"childB");
Thread childA = new Thread(()->{
game.ropeSkipping();
},"childA");
//讓A先跳
childA.start();
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
childB.start();
}
}
如果沒有volatile關鍵字,線程childB執行了stop方法線程childA也是不會停下來的。volatile的作用保證了變量在不同線程間的可見性。
代碼示例2:
public class Game {
private boolean stop = false;
public void stop() {
this.stop = true;
String name = Thread.currentThread().getName();
System.out.println(name + "小孩叫停: ");
}
public void ropeSkipping() {
String name = Thread.currentThread().getName();
int i = 0;
for (;!this.stop;i++) {
System.out.println(name + "小孩跳了: " + i + "次");
}
}
}
示例2去掉了volatile關鍵字,把System.out.println輸出語句放入了循環中,線程childB執行了stop方法線程childA會停下來,因為System.out.println里面有包含了synchronized關鍵字,synchronized也有讓線程間可見性的作用。
除此之外,final關鍵字也能讓變量(常量)線程可見,就不一一舉例了。