可見性,是指線程之間的可見性,一個線程修改的狀態(tài)對另一個線程是可見的。也就是一個線程修改的結果。另一個線程馬上就能看到。 比如:用 volatile
修飾的變量,就會具有可見性。volatile
修飾的變量不允許線程內部緩存和重排序,即直接修改內存。所以對其他線程是可見的。但是這里需要注意一個問題,volatile
只能讓被他修飾內容具有可見性,但不能保證它具有原子性。比如 volatile int a = 0
;之后有一個操作 a++
;這個變量 a
具有可見性,但是 a++
依然是一個非原子操作,也就是這個操作同樣存在線程安全問題。
在 Java
中 volatile
、synchronized
和 final
實現(xiàn)可見性。
即程序執(zhí)行的順序按照代碼的先后順序執(zhí)行。
舉個例子:
int i = 0;
boolean flag = false;
i = 1; //語句1
flag = true; //語句2
上面代碼定義了一個 int
型變量,定義了一個 boolean
類型變量,然后分別對兩個變量進行賦值操作。從代碼順序上看,語句1
是在 語句2
前面的,那么 JVM
在真正執(zhí)行這段代碼的時候會保證 語句1
一定會在 語句2
前面執(zhí)行嗎?不一定,為什么呢?這里可能會發(fā)生指令重排序(Instruction Reorder
)。
指令重排序,一般來說,處理器為了提高程序運行效率,可能會對輸入代碼進行優(yōu)化,它不保證程序中各個語句的執(zhí)行先后順序同代碼中的順序一致,但是它會保證程序最終執(zhí)行結果和代碼順序執(zhí)行的結果是一致的。
Java
語言提供了 volatile
和 synchronized
兩個關鍵字來保證線程之間操作的有序性,volatile
是因為其本身包含“禁止指令重排序”的語義,synchronized
是由“一個變量在同一個時刻只允許一條線程對其進行 lock
操作”這條規(guī)則獲得的,此規(guī)則決定了持有同一個對象鎖的兩個同步塊只能串行執(zhí)行。
volatile
可以保證線程可見性且提供了一定的有序性,但是無法保證原子性。在JVM
底層volatile
是采用“內存屏障”來實現(xiàn)的。
一旦一個共享變量(類的成員變量、類的靜態(tài)成員變量)被 volatile
修飾之后,那么就具備了兩層語義:
Java
語言提供了一種稍弱的同步機制,即 volatile
變量,用來確保將變量的更新操作通知到其他線程。當把變量聲明為 volatile
類型后,編譯器與運行時都會注意到這個變量是共享的,因此不會將該變量上的操作與其他內存操作一起重排序。volatile
變量不會被緩存在寄存器或者對其他處理器不可見的地方,因此在讀取 volatile
類型的變量時總會返回最新寫入的值。
在訪問 volatile
變量時不會執(zhí)行加鎖操作,所以不會造成線程阻塞,所以 volatile
變量是一種比 synchronized
關鍵字更輕量級的同步機制。
當對非 volatile
變量進行讀寫的時候,每個線程先從內存拷貝變量到 CPU
緩存中。如果計算機有多個 CPU
,每個線程可能在不同的 CPU
上被處理,這意味著每個線程可以拷貝到不同的 CPU cache
中。
而聲明變量是 volatile
的,JVM
保證了每次讀變量都從內存中讀,跳過 CPU cache
這一步。
“觀察加入 volatile
關鍵字和沒有加入 volatile
關鍵字時所生成的匯編代碼發(fā)現(xiàn),加入volatile
關鍵字時,會多出一個 lock
前綴指令”,lock
前綴指令實際上相當于一個內存屏障(也成內存柵欄),內存屏障會提供 3
個功能:
CPU
中對應的緩存行無效。volatile
的讀性能消耗與普通變量幾乎相同,但是寫操作稍慢,因為它需要在本地代碼中插入許多內存屏障指令來保證處理器不發(fā)生亂序執(zhí)行。
synchronized
關鍵字是防止多個線程同時執(zhí)行一段代碼,那么就會很影響程序執(zhí)行效率,而 volatile
關鍵字在某些情況下性能要優(yōu)于 synchronized
,但是要注意 volatile
關鍵字是無法替代 synchronized
關鍵字的,因為 volatile
關鍵字無法保證操作的原子性。通常來說,使用 volatile
必須具備以下 2
個條件:
實際上,這些條件表明,可以被寫入 volatile
變量的這些有效值于任何程序的狀態(tài),包括變量的當前狀態(tài)。事實上,我的理解就是上面的 2
個條件需要保證操作是原子性操作,才能保證使用volatile
關鍵字的程序在并發(fā)時能夠正確執(zhí)行。
volatile boolean flag = false;
//線程1
while(!flag){
doSomething();
}
//線程2
public void setFlag() {
flag = true;
}
根據(jù)狀態(tài)標記,終止線程。
class Singleton{
private volatile static Singleton instance = null;
private Singleton() {
}
public static Singleton getInstance() {
if(instancenull) {
synchronized (Singleton.class) {
if(instancenull)
instance = new Singleton();
}
}
return instance;
}
}
本文只是對 volatile
的簡單使用進行一個簡單的總結,更多的學習可以參照文章:
本人也收藏了一份Java面試核心知識點來應付面試,借著這次機會可以送給我的讀者朋友們:
Java面試核心知識點
一共有30個專題,足夠讀者朋友們應付面試啦,也節(jié)省朋友們去到處搜刮資料自己整理的時間!
Java面試核心知識點
Java面試核心知識點
一共有30個專題,足夠讀者朋友們應付面試啦,也節(jié)省朋友們去到處搜刮資料自己整理的時間!
[外鏈圖片轉存中…(img-17iUckTc-17232967596)]
Java面試核心知識點
本文已被收錄
因篇幅問題不能全部顯示,請點此查看更多更全內容
Copyright ? 2019- 91gzw.com 版權所有 湘ICP備2023023988號-2
違法及侵權請聯(lián)系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com
本站由北京市萬商天勤律師事務所王興未律師提供法律服務