![](/img/trans.png)
[英]Possibly blocking call in non-blocking context could lead to thread starvation. Why?
[英]Starvation in non-blocking approaches
我已經閱讀了一段時間的非阻塞方法。 這是所謂的無鎖計數器的一段代碼。
public class CasCounter {
private SimulatedCAS value;
public int getValue() {
return value.get();
}
public int increment() {
int v;
do {
v = value.get();
}
while (v != value.compareAndSwap(v, v + 1));
return v + 1;
}
}
我只是想知道這個循環:
do {
v = value.get();
}
while (v != value.compareAndSwap(v, v + 1));
人們說:
因此,它會一次又一次嘗試,直到所有其他試圖更改值的線程都這樣做為止。 這是無鎖的,因為沒有使用鎖,但不是無鎖的,因為它可能必須重試一次(這種情況很少)(一次以上)(非常罕見)。
我的問題是:
他們怎么能這樣確定? 對我而言,除非JVM有一些特殊的機制可以解決這個問題,否則我看不出為什么這個循環不會無限長。
編輯 :我想我現在有一個滿意的答案。 令我感到困惑的是“ v!= compareAndSwap”。 在實際代碼中,如果該值等於比較的表達式,則CAS返回true。 因此,即使第一個線程在get和CAS之間中斷,第二個線程也會成功進行交換並退出該方法,因此第一個線程將能夠執行CAS。
當然,如果兩個線程無數次調用此方法,則其中一個根本不會獲得運行CAS的機會,尤其是在優先級較低的情況下,但這是操作的風險之一。不公平的鎖定(但是概率很低)。 就像我說的那樣,隊列機制將能夠解決此問題。
很抱歉最初的錯誤假設。
循環可以是無限的(因為它可以為您的線程產生飢餓),但是這種情況發生的可能性很小。 為了使您挨餓,您需要一些其他線程來成功更改您要在讀取和存儲之間更新的值,並使其重復發生。
可能會編寫代碼來觸發飢餓,但是對於實際程序而言,這不太可能發生。
當您認為自己不會經常發生寫沖突時,通常使用比較和交換。 假設您更新時有50%的“未命中”機會,那么有25%的機會您會在兩個循環中錯過,而少於0.1%的機會是沒有更新會在10個循環中成功。 對於現實世界的示例,50%的未命中率非常高(基本上沒有做任何事情都只能進行更新),並且隨着未命中率的降低,可以說1%,那么兩次嘗試不成功的風險僅為0.01%,而3次失敗嘗試0.0001%。
用法類似於以下問題
將變量a設置為0,並讓兩個線程同時以a = a + 1一百萬次對其進行更新。 最后,a可能有1000000(由於覆蓋而丟失所有其他更新)和2000000(沒有覆蓋任何更新)之間的任何答案。
您越接近2000000,CAS使用的可能性就越大,因為這意味着CAS通常會看到期望值並能夠使用新值進行設置。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.