原子性
原子性指的是一個或者多個操作在 CPU 執行的過程中不被中斷的特性
線程切換 帶來的原子性問題
Java 并發程式都是基于多線程的,作業系統為了充分利用CPU的資源,将CPU分成若幹個時間片,在多線程環境下,線程會被作業系統排程進行任務切換。
為了直覺的了解什麼是原子性,我們看下下面哪些操作是原子性操作
int count = ; //1
count++; //2
int a = count; //3
複制代碼
上面展示語句中,除了語句1是原子操作,其它兩個語句都不是原子性操作,下面我們來分析一下語句2
其實語句2在執行的時候,包含三個指令操作
- 指令 1:首先,需要把變量 count 從記憶體加載到 CPU的寄存器
- 指令 2:之後,在寄存器中執行 +1 操作;
- 指令 3:最後,将結果寫入記憶體
對于上面的三條指令來說,如果線程 A 在指令 1 執行完後做線程切換,線程 A 和線程 B 按照下圖的序列執行,那麼我們會發現兩個線程都執行了 count+=1 的操作,但是得到的結果不是我們期望的 2,而是 1。
作業系統做任務切換,可以發生在任何一條CPU 指令執行完
有序性
有序性指的是程式按照代碼的先後順序執行
**編譯優化 **帶來的有序性問題
為了性能優化,編譯器和處理器會進行指令重排序,有時候會改變程式中語句的先後順序,比如程式:
a = ; //1
b = ; //2
c = a + b; //3
複制代碼
編譯器優化後可能變成
b = ; //1
a = ; //2
c = a + b; //3
複制代碼
在這個例子中,編譯器調整了語句的順序,但是不影響程式的最終結果
synchronized(具有有序性、原子性、可見性)表示鎖在同一時刻隻能由一個線程進行擷取,當鎖被占用後,其他線程隻能等待。
在單例模式的實作上有一種雙重檢驗鎖定的方式(Double-checked Locking)
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 是沒有初始化過的。
現行的比較通用的做法就是采用靜态内部類的方式來實作
public class SingletonDemo {
private SingletonDemo() {
}
private static class SingletonDemoHandler{
private static SingletonDemo instance = new SingletonDemo();
}
public static SingletonDemo getInstance() {
return SingletonDemoHandler.instance;
}
}
複制代碼
可見性
可見性指的是當一個線程修改了共享變量後,其他線程能夠立即得知這個修改
緩存 導緻的可見性問題
首先我們來看一下Java記憶體模型(JMM)
- 我們定義的所有變量都儲存在
中主記憶體
- 每個線程都有自己
,裡面儲存該線程使用到的變量的副本(主記憶體中該變量的一份拷貝)獨立的工作記憶體
- 線程對共享變量所有的操作都必須在自己的工作記憶體中進行,不能直接從主記憶體中讀寫(不能越級)
- 不同線程之間也無法直接通路其他線程的工作記憶體中的變量,線程間變量值的傳遞需要通過主記憶體來進行。(同級不能互相通路)
共享變量可見性的實作原理:
線程1對共享變量的修改要被線程2及時看到的話,要經過如下步驟:
- 把工作記憶體1中更新的變量值重新整理到主記憶體
- 把主記憶體中的變量的值更新到工作記憶體2中
可以使用 synchronized 、volatile 、final 來保證可見性
歡迎關注公衆号
轉載于:https://juejin.im/post/5c7dfc925188251b8c769f69