(Java多執行緒系列七)Java記憶體模型和執行緒的三大特性
Java記憶體模型和執行緒的三大特性
多執行緒有三大特性:原子性、可見性、有序性
1、Java記憶體模型
Java記憶體模型(Java Memory Model ,JMM),決定一個執行緒對共享變數的寫入時,能對另一個執行緒可見。從抽象的角度來看,JMM定義了執行緒和主記憶體之間的抽象關係:執行緒之間的共享變數儲存在主記憶體(main memory)中,每個執行緒都有一個私有的本地記憶體(local memory),本地記憶體中儲存了該執行緒以讀/寫共享變數的副本。本地記憶體是JMM的一個抽象概念,並不真實存在。
用一張圖表示Java記憶體模型
2、原子性
原子性即一個操作或多個操作,要麼全部執行並且執行過程不被任何因素打斷,要麼就都不執行。
一個經典的例子就是資料庫儲存的事務。原子性其實就是保證資料一致、執行緒安全的一部分。
Synchronized、lock可以解決執行緒原子性問題
3、可見性
當多個執行緒訪問同一個變數時,一個執行緒修改了這個變數的值,其他執行緒能夠立即看得到修改的值。
若兩個執行緒在不同的cpu,那麼執行緒1改變了i的值還沒重新整理到主存,執行緒2又使用了i,那麼這個i值肯定還是之前的,執行緒1對變數的修改其他執行緒沒看到,這就是可見性問題。
Volatile可以解決執行緒可見性問題
4、有序性
程式執行的順序按照程式碼的先後順序執行。
一般來說處理器為了提高程式執行效率,可能會對輸入程式碼進行優化,它不保證程式中各個語句的執行先後順序同程式碼中的順序一致,但是它會保證程式最終執行結果和程式碼順序執行的結果是一致的。
而在多執行緒就不一定了,所以我們在多執行緒程式設計時就得考慮這個問題了。
5、volatile關鍵字可以解決執行緒之間可見性的問題
class ThreadDemo extends Thread { boolean flag; ThreadDemo(boolean flag) { this.flag = flag; } @Override public void run() { System.out.println(getName() + "執行緒開始執行。。。"); while (flag) { } System.out.println(getName() + "執行緒已經結束。。。"); } public void stopThread() { this.flag = false; } } public class VolatileThreadDemo { public static void main(String[] args) { ThreadDemo threadDemo = new ThreadDemo(true); threadDemo.start(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } threadDemo.stopThread(); } }
先來看以上的程式碼,在主執行緒中呼叫
threadDemo.stopThread()
方法停止執行緒,看上去邏輯沒有問題,但是我們會發現執行緒沒有停止。
注意:有的同學可能在測試上面程式碼的時候程式可以正常退出。那是因為你的JVM沒有優化造成的!
造成執行緒沒有停止的原因是
while(flag)
中的flag是線上程執行的“工作記憶體”中獲取的,而不是從“主記憶體”中獲取的,這就造成了我們在主執行緒中改變flag的值對於子執行緒中不生效。只要在flag前加volatile關鍵字,強制執行緒每次讀取該值的時候都去“主記憶體”中取值,就能解決我們的問題。
package com.littlestones.volatiledemo;
/**
* @program: JavaThreadLearn
* @description: volatile示例
* @author: Leil
* @create: 2019-12-24 15:22
*/
class ThreadDemo extends Thread {
volatile boolean flag;
ThreadDemo(boolean flag) {
this.flag = flag;
}
@Override
public void run() {
System.out.println(getName() + "執行緒開始執行。。。");
while (flag) {
}
System.out.println(getName() + "執行緒已經結束。。。");
}
public void stopThread() {
this.flag = false;
}
}
public class VolatileThreadDemo {
public static void main(String[] args) {
ThreadDemo threadDemo = new ThreadDemo(true);
threadDemo.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
threadDemo.stopThread();
}
}
注意:volatile關鍵字只能解決執行緒的可見性問題,不能解決執行緒的原子性問題