首先volatile關鍵字有線程安全問題。
volatile有一個專業術語:保證了可見性,不保證原子性。
首先不使用volatile關鍵字,看看有什麼效果
public class NotUseVolatile implements Runnable {
/**
* 定義一個非volatile修飾的變量
*/
private boolean falg = true;
/**
* 線程執行代碼
*/
public void run() {
System.out.println("子線程開始執行");
while (falg) {
}
System.out.println("子線程執行結束");
}
/**
* 生成get set方法
*/
public boolean isFalg() {
return falg;
}
public void setFalg(boolean falg) {
this.falg = falg;
}
}
public class Test {
public static void main(String[] args) throws InterruptedException {
NotUseVolatile not = new NotUseVolatile();
Thread thread = new Thread(not, "不使用volatile關鍵字的線程");
thread.start();
/**
* 休眠3秒: 如果不休眠,模擬不出這個效果。
*/
Thread.sleep(3000);
not.setFalg(false);
System.out.println("flag值已修改爲false");
Thread.sleep(1000);
System.out.println("flag值爲:" + not.isFalg());
/**
* 打印結果:
*
* 子線程開始執行
* flag值已修改爲false
* flag值爲:false
*
* ------------------------------------
*
* 有沒有發現,flag的值已經修改了,但是線程依然沒有結束 ?
*
* 這是爲什麼呢 ?
*
* 這裏就要說到Java的內存模型了。
*
* 注意:
* Java內存模型:屬於線程安全的一個知識點。
* Java內存結構:屬於Jvm的內存分配問題,堆、棧、方法區等。
*
* 在java內存模型中,有主內存 和 線程內存兩個概念:
* 全局變量在主內存中,當線程操作全局變量的時候,首先是把全局變量的值複製到線程內存中去,然後線程內存修改之後,再通知主內存修改數據。
* 所以這裏的flag值有機率不會被修改。
*
* 但是如果我們要修改線程中的值,怎麼辦呢 ?
* 就是再全局變量上加上volatile關鍵字,當加上了volatile關鍵字的全局變量被修改後,會去通知線程內存,最後線程內存的值也會修改爲主內存所改變的值。
*/
}
}
使用volatile關鍵字:
public class UseVolatile implements Runnable {
/**
* 定義一個volatile修飾的變量:
*
* 注意:volatile關鍵字,只保證線程之間的可見性,但是不保證原子性(也就是說線程不安全)
*/
private volatile boolean falg = true;
/**
* 線程執行代碼
*/
public void run() {
System.out.println("子線程開始執行");
while (falg) {
}
System.out.println("子線程執行結束");
}
/**
* 生成get set方法
*/
public boolean isFalg() {
return falg;
}
public void setFalg(boolean falg) {
this.falg = falg;
}
}
public class Test {
public static void main(String[] args) throws InterruptedException {
UseVolatile not = new UseVolatile();
Thread thread = new Thread(not, "使用volatile關鍵字的線程");
thread.start();
/**
* 休眠3秒
*/
Thread.sleep(3000);
not.setFalg(false);
System.out.println("flag值已修改爲false");
Thread.sleep(1000);
System.out.println("flag值爲:" + not.isFalg());
/**
* 打印結果:
* 線程開始執行
* flag值已修改爲false
* 子線程執行結束
* flag值爲:false
*
* ------------------------------------
*
* 使用了volatile關鍵字後,就達到我們的理想效果了。
*
* 注意:volatile有線程安全問題。
*/
}
}
所以 volatile的作用是:
每次線程讀取全局變量前必須先從主內存刷新最新的值。每次線程寫入後必須立即將修改的全局變量同步回主內存當中。