3 回答
TA貢獻1852條經驗 獲得超1個贊
當你想讓一個成員變量被多個線程訪問但不需要復合原子性(不確定這是否是正確的術語)時,你基本上使用它。
class BadExample {
private volatile int counter;
public void hit(){
/* This operation is in fact two operations:
* 1) int tmp = this.counter;
* 2) this.counter = tmp + 1;
* and is thus broken (counter becomes fewer
* than the accurate amount).
*/
counter++;
}}以上是一個不好的例子,因為你需要復合原子性。
class BadExampleFixed {
private int counter;
public synchronized void hit(){
/*
* Only one thread performs action (1), (2) at a time
* "atomically", in the sense that other threads can not
* observe the intermediate state between (1) and (2).
* Therefore, the counter will be accurate.
*/
counter++;
}}現在來看一個有效的例子:
class GoodExample {
private static volatile int temperature;
//Called by some other thread than main
public static void todaysTemperature(int temp){
// This operation is a single operation, so you
// do not need compound atomicity
temperature = temp;
}
public static void main(String[] args) throws Exception{
while(true){
Thread.sleep(2000);
System.out.println("Today's temperature is "+temperature);
}
}}現在,為什么你不能使用private static int temperature?實際上你可以(在某種意義上說你的程序不會爆炸或者其他東西),但是temperature另一個線程的改變可能對主線程“可見”,也可能不是“可見”。
基本上這意味著你的應用甚至可能。Today's temperature is 0如果你不使用volatile,它會永遠寫作(在實踐中,這個值最終會變得可見。但是,你不應該冒險在必要時不使用volatile,因為它可能會導致令人討厭的錯誤(由完全構造的對象等引起)。 )。
如果將volatile關鍵字放在不需要的東西上volatile,它不會影響代碼的正確性(即行為不會改變)。在性能方面,它將取決于JVM實現。從理論上講,由于編譯器無法進行重新排序優化,不得不使CPU緩存等無效,因此可能會出現微小的性能下降,但再次編譯器可以證明多個線程無法訪問您的字段并刪除volatile關鍵字的影響完全并將其編譯為相同的指令。
編輯:
回復此評論:
好的,但是為什么我們不能讓todaysTemperature同步并為溫度創建一個同步的getter?
你可以,它會表現正常。您可以使用的任何東西volatile都可以完成synchronized,但反之亦然。volatile如果可以,您可能會有兩個理由:
減少錯誤:這取決于上下文,但在許多情況下使用
volatile不太容易出現并發錯誤,例如在持有鎖時阻塞,死鎖等。性能
volatile更高:在大多數JVM實現中,可以具有更高的吞吐量和更好的延遲。然而,在大多數應用中,差異太小而無關緊要。
TA貢獻1829條經驗 獲得超9個贊
volatile 關鍵字保證volatile變量的值總是從主內存中讀取,而不是從Thread的本地緩存中讀取。
來自java并發教程:
使用volatile變量可以降低內存一致性錯誤的風險,因為對volatile變量的任何寫入都會建立與之后讀取相同變量的先發生關系
這意味著對volatile變量的更改始終對其他線程可見。它還意味著當線程讀取volatile變量時,它不僅會看到volatile的最新更改,還會看到導致更改的代碼的副作用。
關于你的查詢:
我怎么知道何時應該標記變量volatile?在確定多線程代碼中哪些變量應該是易變的時,有哪些經驗法則?
如果您認為所有讀者線程始終獲得變量的最新值,則必須將變量標記為 volatile
如果您有一個編寫器線程來修改變量和多個讀取器線程的值來讀取變量的值,那么volatile修飾符可以保證內存的一致性。
如果您有多個線程來寫和讀取變量,volatile單獨使用修飾符并不能保證內存一致性。你必須synchronize在代碼或使用高水平并發結構,如Locks,Concurrent Collections,Atomic variables等。
添加回答
舉報
