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關鍵字只能解決線程的可見性問題,不能解決線程的原子性問題