區域性變數保證執行緒安全

2021-10-05 03:30:28 字數 1742 閱讀 3450

首先來看string這個類的hashcode方法,如下

public

inthashcode()

hash = h;

/* **② */

}return

(h);

/* **③ */

}

hashstring類的乙個屬性,可以看到這邊首先是**①讀取了本地屬性的值,並且賦值給區域性變數h。並且使用h進行了業務邏輯的判斷。如果h沒有值的話,就進行 hash 值的生成,並且賦值到h上,並且在**②處賦值給了屬性hash。最終返回的,也是區域性變數h的值。那麼上述的**能否修改為下面的模式

public

inthashcode()

hash = h;

}return

(hash)

;/* **② */

}

修改的**沒有區域性變數,直接使用屬性本身來操作。

答案是否定的,因為這種寫法是執行緒不安全的,可能導致方法的返回值是 0 。似乎有點費解,因為如果hash值為0 ,則**會進入迴圈體,對hash值進行更新。所以乍看之下,無論如何是不會返回 0 的。

上述的理解邏輯,在單執行緒環境下,是正確的。但是這段**工作在多執行緒環境。實際上,上述**有兩次對hash值的讀取,分別是**①和②。可能會出現一種情況,在**①處,讀取到hash值不為 0 ,在**②處,讀取到hash值為0,並且以此為結果返回了。顯然此時這種結果是錯誤的。

要理解這種場景的發生需要從 jmm 的規則談起。首先,兩個讀取之間是沒有因果關係的,因此不存在第乙個對變數的讀取觀察到了值,第二個對該變數的讀取也要觀察到這個值。其次,在 jmm 中,對乙個變數的讀取操作允許其觀察最後一次到對該變數的寫入,只要沒有 hb 關係來阻止這個讀取的觀察效果。此外,物件屬性的預設值也是由寫入動作觸發的。這意味著對hash值的寫入有兩個地方,乙個在於物件構造時,乙個在於其他執行緒對hash值的寫入。由於這兩個寫入沒有 hb 關係,因此對hash的讀取可能讀取到任意乙個寫入的結果。所以,可能會出現的情況是在**①處讀取到了其他執行緒對hash值的寫入,因此跳過了內部的寫入邏輯。而在**②處再次讀取hash值,此時讀取到了物件構造時對hash預設值的寫入,導致返回 0 。

從 jmm 規則角度是最正確的理解,但是為了形象的想象這一切如何發生,我們可以將上面的程式修改如下

public

inthashcode()

a = hash = h;

}return

(a);

/* **② */

}

實際上,這的確是在執行**邏輯的時候,一種可能的**重排序變種。假定一開始hash值為0,則a為 0 。在if判斷的時候,hash讀取到了其他執行緒寫入的值,因此沒有執行計算邏輯,最終返回了a的值,也就是 0 。

DLL 執行緒區域性變數

1.用 declspec thread 建立執行緒區域性變數 declspec thread int tls count 0 注意事項 當用 declspec thread 宣告執行緒區域性變數的時候,應注意以下事項 1 只能用來宣告或者定義具有static作用域的變數,而不能用來宣告或者定義區域性...

關於區域性變數就是執行緒安全的理解

網上看到好多定論說是區域性變數就是執行緒安全的。但是我覺得對大家都造成了一些誤解,例如 以下 package test public class demo 5 售票視窗類 class ticketwindow implements runnable if me catch interruptedex...

為什麼區域性變數是執行緒安全的

例如,有三個方法 a b c,他們的呼叫關係是 a b c a 呼叫 b,b 呼叫 c 在執行時,會構建出下面這樣的呼叫棧。每個方法在呼叫棧裡都有自己的獨立空間,稱為棧幀,每個棧幀裡都有對應方法需要的引數和返回位址。當呼叫方法時,會建立新的棧幀,並壓入呼叫棧 當方法返回時,對應的棧幀就會被自動彈出。...