Volatile詳解

volatile

簡介

java虛擬機提供的輕量級的同步機制java

1.保證可見性

添加volatile關鍵詞之後,當線程A改變了a的值,那麼其它調用a的值其它線安全

程,就會獲得通知多線程

 1 class Mydata{
 2     volatile int a=0;
 3     public void resizeA() {
 4         this.a=60;
 5     }
 6 }
 7 public class VolatileDemo {
 8     public static void main(String[] args) {
 9         Mydata mydata = new Mydata();
10         new Thread(()->{
11             System.out.println(Thread.currentThread().getName()+"\t com in");
12             try {
13                 TimeUnit.SECONDS.sleep(3);//延遲3秒
14             }catch (InterruptedException e){
15                 e.printStackTrace();
16             }
17             mydata.resizeA();
18             System.out.println(Thread.currentThread().getName()+"\t updata number value:"+mydata.a);
19         },"aaa").start();
20 21         while (mydata.a == 0) {
22             //等待
23         }
24         System.out.println(Thread.currentThread().getName()+"\t over"+mydata.a);
25     }
26 27 }

 

不加volatile 結果1性能

aaa com inthis

aaa updata number value:60spa

不加volatile 線程main 獲得的值一直是a=0,因此一直套在死循環中出不來線程

 

加volatile 結果2code

aaa com in對象

aaa updata number value:60blog

main over60

加了volatile 線程aaa改了a的值,立馬刷新到主內存中去,並通知其它線程

2.不保證原子性

當多個線程同時拿到值後,同時返回到主內存時,一個線程佔用了主內存,將值寫入,其它線程寫主內存,掛起,當通知其它線程時,其它線程就會寫進來,從而致使數據損失;

 1 class Mydata{
 2     volatile int a=0;
 3     public void resizeA() {
 4         this.a++;
 5     }
 6 }
 7 public class VolatileDemo {
 8     public static void main(String[] args) {
 9         Mydata mydata = new Mydata();
10         for (int i = 0; i < 200; i++) {
11             for (int j = 0; j <100 ; j++) {
12                 new Thread(()->{
13                     mydata.resizeA();
14                 },"aaa").start();
15             }
16         }
17         while (Thread.activeCount() > 2) {//還剩2個,main和gc
18             Thread.yield();//讓出cpu
19         }
20         System.out.println(Thread.currentThread().getName()+"\t  "+mydata.a);
21     }
22 23 }

 

計算的數值必定要大,很難發生數據損失

3.禁止指令重排

什麼是指令重排?

計算機爲了提升性能在執行程序是,編譯器和處理器會對程序執行指令重排;

 

指令重排的要求:

  1. 單線程的狀況下不會改變最終的執行結果

  2. 必須考慮數據的依賴性

案例

 1 public class demo{
 2     int a=0;
 3     boolean flag=false;
 4     public void test(){
 5         a=1;//語句1
 6         flag=true;//語句2
 7     }
 8     //由於沒有依賴關係,編譯器可能將1和2換位置
 9     //多線程時,因爲速度太快,先執行2,1尚未執行,就執行了test2裏面的方法,致使結果沒法預測
10     public void test2(){
11         if(flag){
12             a+=5;
13            
14         }
15     }
16 }

 

volatile的應用案例

單例模式

 1 public class demo{
 2     int a=0;
 3     boolean flag=false;
 4     public void test(){
 5         a=1;//語句1
 6         flag=true;//語句2
 7     }
 8     //由於沒有依賴關係,編譯器可能將1和2換位置
 9     //多線程時,因爲速度太快,先執行2,1尚未執行,就執行了test2裏面的方法,致使結果沒法預測
10     public void test2(){
11         if(flag){
12             a+=5;
13            
14         }
15     }
16 }

 

synchronized不能禁止指令重排,因此要加volatile來禁止指令重排

由於實例化會發生三個步驟

1.分配內存空間

2.初始化對象

3.指向instance 此時!=null

由於2 3沒有數據依賴,因此可能發生指令重排,3先執行,2後,而此時2實際爲空對象,因此多線程,在3執行前,使用對象會發生異常;

 

 

拓展:不使用線程鎖來實現懶漢單例模式同時保證線程安全

 1 //靜態內部類
 2 public class Singleton4 {
 3     private Singleton4(){}
 4     private static class Inner{
 5         private static final Singleton4 INSTANCE=new Singleton4();
 6     }
 7     public static Singleton4 getInstance(){
 8         return Inner.INSTANCE;
 9     }
10 }
相關文章
相關標籤/搜索