Java並發之原子性,可見性,有序性


原子性

​原子性指的是一個或者多個操作在 CPU 執行的過程中不被中斷的特性

在多線程情況下,線程會被操作系統調度進行任務切換,占有CPU時間片段的就執行,否則就阻塞

 

 

java中對基礎類型的變量賦值是原子性的,int  a = 1 ;

但是像這種語句 count++; 在執行的時候,包含3的指令操作

指令 1:首先,需要把變量 count 從內存加載到 CPU的寄存器

指令 2:在寄存器中執行 +1 操作

指令 3:最后,將結果寫入內存

在一個線程里執行是沒有問題的,但是在多線程情況下,會引發數據不一致問題

對於上面的三條指令來說,如果線程 A 在指令 1 執行完后做線程切換,線程 A 和線程 B 按照下圖的序列執行,那么我們會發現兩個線程都執行了 count+=1 的操作,但是得到的結果不是我們期望的 2,而是 1。

 

 

 

 

 

可見性

 

 可見性指的是當一個線程修改了共享變量后,其他線程能夠立即得知這個修改,volatile可以保證可見性

 

 

 

有序性

​ 有序性指的是程序按照代碼的先后順序執行

 為了性能優化,編譯器和處理器會可能進行指令重排序,有時候會改變程序中語句的先后順序

a = 5; //1 b = 20; //2 c = a + b; //3

編譯器優化后可能變成
b = 20; //1 a = 5; //2 c = a + b; //3
編譯器調整了語句的順序,在單線程中並沒有什么影響,但是在多線程中可能會出現異常
單例模式有一種實現是
public class Singleton {
  static Singleton instance;
  static Singleton getInstance(){
    if (instance == null) {
      synchronized(Singleton.class) {
        if (instance == null)
          instance = new Singleton();
        }
    }
    return instance;
  }
}
 
        

我們先看 instance = new Singleton() 的未被編譯器優化的操作

  • 指令 1:分配一塊內存 M;
  • 指令 2:在內存 M 上初始化 Singleton 對象;
  • 指令 3:然后 M 的地址賦值給 instance 變量。

編譯器優化后的操作指令

  • 指令 1:分配一塊內存 M;
  • 指令 2:將 M 的地址賦值給 instance 變量;
  • 指令 3:然后在內存 M 上初始化 Singleton 對象。
現在有A,B兩個線程,我們假設線程A先執行getInstance()方法,當執行編譯器優化后的操作指令2時(此時候未完成對象的初始化),這時候發生了線程切換,那么線程B進入,剛好執行到第一次判斷instance == null 會發現instance
不等於null了,所以直接返回instance,而此時instance是沒有初始化的

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM