volatile關鍵字是Java中提供的另一種解決可見性和有序性問題的方案。
對volatile變量的單次讀/寫操作可以保證原子性的。
但是並不能保證i++這種操作的原子性,因爲本質上i++是讀、寫兩次操作。
在訪問volatile變量時不會執行加鎖操作,因此也就不會使執行線程阻塞,因此volatile變量是一種比sychronized關鍵字更輕量級的同步機制
當且僅當滿足以下所有條件時,才應該使用volatile變量:
對變量的寫操作不依賴變量的當前值,或者你能確保只有單個線程更新變量的值。
該變量不會與其他變量一起納入不變性條件中。
在訪問變量時不需要加鎖。
volatile 關鍵字,使一個變量在多個線程間可見
A B線程都用到一個變量,java默認使A線程中保留一份copy,這樣如果B線程修改了該變量,則A線程未必知道
可以通過下圖來表示線程的存儲:
使用volatile關鍵字,會讓所有線程都會讀到變量的修改值
volatile 並不能保證多個線程共同修改running變量時所帶來的不一致問題,也就是說volatile不能代替synchonized
public class T6 {
/*volatile*/ boolean running = true;
public void m()
{
System.out.println("start");
while(running)
{
//加入sleep 可能抽出時間來更新緩衝區中的值
// try {
// Thread.sleep(1000);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
}
System.out.println("end");
}
public static void main(String[] args) {
T6 t6 = new T6();
new Thread(()->t6.m()).start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
t6.running = false;
}
}
程序運行結果:
可以看到線程1陷入了死循環,也就是說線程1並沒有收到主線程更新running值的操作,這是因爲線程1一直在執行while循環,雖然主線程向內存寫入了running的值,但是線程1沒有時間去讀內存中running的值,所以會一直陷入死循環。
當在源程序的基礎上在while循環中加入睡眠1秒鐘時的運行結果:
可以看到因爲睡眠了1秒鐘,線程1趁機去內存中更新了running的值。
在源程序的基礎上在running上添加volatile關鍵字的運行結果:
由於加上了volatile關鍵字,使得主線程更新running值的同時,線程1會收到通知,去更新自己緩衝區中running變量的值,於是while循環就退出了。
volatile 並不能保證多個線程共同修改running變量時所帶來的不一致問題,也就是說volatile不能代替synchonized。
synchnized 既可以保障原子性和可見性,volatile只能保障可見性
public class T7 {
volatile int count = 0;
/*synchronized*/ void m()
{
for (int i=0;i<10000;i++) count++;
}
public static void main(String[] args) {
T7 t7 = new T7();
List<Thread> threadList= new ArrayList();
for (int i=0;i<10;i++)
{
threadList.add(new Thread(()->t7.m(),"thread"+i));
}
//啓動線程
threadList.forEach((o)->o.start());
//等待線程結束
threadList.forEach((o)->{
try {
o.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
System.out.println(t7.count);
}
}
程序運行結果:
可以看到count的值比正確值100000差了很多,因爲是多個線程同時執行count++操作,並且count++操作是一個讀->寫的過程,多個線程同時進行讀->寫的操作就會出現加的值少的問題。
給m方法加上synchronized關鍵字後的運行結果:
因爲給方法加了鎖,所以使線程線性執行,所以不會出現問題。
解決同樣問題的更高效的方法是使用AtomXXXX類
AtomXXXX類本身方法都是原子性的,但不能保證多個方法連續調用是原子性的。