volatile是什麼?
標記變量是“易變的”,要求線程使用變量時,必須直接從主存獲取;線程修改完畢後,必須保證一氣呵成的寫回主存。
volatile爲什麼?
需要先了解java內存模型。
在主內存與線程之間,爲了提高速度,又多了工作內存。工作內存與主內存的關係,類似電腦內存與硬盤的關係,緩存數據,提高速度,但可能存在不一致問題。
volatile希望在一定程度上保證一致性。
但在併發的3大特徵裏,volatile實現了可見性。即如果修改,修改結果對其他線程可見。
volatile如何實現的?
待補充。
volatile的侷限
雖然volatile保證了從主內存讀用和回寫到主內存,但併發情況下,還是可能發生2個線程先後腳的讀寫共享變量,所以不能保證線程安全。
getstatic指令把increase的值拿到了操作棧的頂部,此時由於volatile的規則,該值是正確的。
iconst_1和iadd指令在執行的時候increase的值很有可能已經被其他線程加大,此時棧頂的值過期。
putstatic指令接着把過期的值同步回主存,導致了最終結果較小。
實驗
package com.master;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Created by 10171512 on 2020/4/17.
* 1)驗證volatile不能保證線程安全
* 1.共享變量:類靜態
* 2.多線程讀寫
* 2)對照組:普通變量和原子變量
*/
public class D13VolatileTest extends Thread{
private static volatile int volInt=0;
private static int normalInt=0;
private static AtomicInteger atomInt=new AtomicInteger();
private void increment(){
volInt++;
normalInt++;
atomInt.incrementAndGet();
}
@Override
public void run() {
for (int i = 0; i <10000 ; i++) {
increment();
}
}
public static void get() {
System.out.println("volInt:"+volInt+"; normalInt:"+normalInt+"; atomInt:"+atomInt);
}
public static void main(String[] args) {
Thread[] threads = new Thread[10];
for (int i = 0; i <threads.length ; i++) {
threads[i]=new D13VolatileTest();
threads[i].start();
/*法3:join().但線程會變成穿行執行,不會再出現共享變量不一致。*/
/*try {
threads[i].join();
} catch (InterruptedException e) {
System.out.println(e);
}*/
System.out.println(threads[i].isDaemon());
}
/*子線程會在mian線程退出時退出,無論子線程是否執行完畢,解決方法:
* 法1.判斷線程組的線程數*/
/*while (Thread.activeCount() > 2) {
Thread.yield();
}*/
/*法2:主線程等待*/
try {
Thread.sleep(5_000);
} catch (InterruptedException e) {
System.out.println(e);
}
D13VolatileTest.get();
}
}
多次執行:
volInt:94555; normalInt:98774; atomInt:100000
volInt:89376; normalInt:95213; atomInt:100000
volInt:83797; normalInt:89708; atomInt:100000
可見:
1)volatile不能保證線程安全。
2)volatile變量不一定比普通變量更安全。
參考:
徹底理解volatile關鍵字