一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|JavaScript|易語言|

服務器之家 - 編程語言 - Java教程 - 深入講解我們說的CAS自旋鎖到底是什么

深入講解我們說的CAS自旋鎖到底是什么

2021-05-04 11:53風箏 Java教程

這篇文章主要給大家介紹了關于我們說的CAS自旋鎖到底是什么的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧

什么是自旋鎖

說道自旋鎖就要從多線程下的鎖機制說起,由于在多處理器系統環境中有些資源因為其有限性,有時需要互斥訪問(mutual exclusion),這時會引入鎖的機制,只有獲取了鎖的進程才能獲取資源訪問。即每次只能有且只有一個進程能獲取鎖,才能進入自己的臨界區,同一時間不能兩個或兩個以上進程進入臨界區,當退出臨界區時釋放鎖。

設計互斥算法時總是會面臨一種情況,即沒有獲得鎖的進程怎么辦?

通常有2種處理方式:

一種是沒有獲得鎖的調用者就一直循環在那里看是否該自旋鎖的保持者已經釋放了鎖,這就是本文的重點——自旋鎖。他不用將線城阻塞起來(non-blocking)。

另一種是沒有獲得鎖的進程就阻塞(blocking)自己,繼續執行線程上的其他任務,這就是 ——互斥鎖(包括內置鎖synchronized還有reentrantlock等等)。

引言

cas(compare and swap),即比較并交換,也是實現我們平時所說的自旋鎖或樂觀鎖的核心操作。

它的實現很簡單,就是用一個預期的值和內存值進行比較,如果兩個值相等,就用預期的值替換內存值,并返回 true。否則,返回 false。

保證原子操作

任何技術的出現都是為了解決某些特定的問題, cas 要解決的問題就是保證原子操作。原子操作是什么,原子就是最小不可拆分的,原子操作就是最小不可拆分的操作,也就是說操作一旦開始,就不能被打斷,知道操作完成。在多線程環境下,原子操作是保證線程安全的重要手段。舉個例子來說,假設有兩個線程在工作,都想對某個值做修改,就拿自增操作來說吧,要對一個整數 i 進行自增操作,需要基本的三個步驟:

1、讀取 i 的當前值;

2、對 i 值進行加 1 操作;

3、將 i 值寫回內存;

假設兩個進程都讀取了 i 的當前值,假設是 0,這時候 a 線程對 i 加 1 了,b 線程也 加 1,最后 i 的是 1 ,而不是 2。這就是因為自增操作不是原子操作,分成的這三個步驟可以被干擾。如下面這個例子,10個線程,每個線程都執行 10000 次 i++ 操作,我們期望的值是 100,000,但是很遺憾,結果總是小于 100,000 的。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
static int i = 0;
public static void add(){
i++;
}
 
private static class plus implements runnable{
@override
public void run(){
for(int k = 0;k<10000;k++){
add();
}
}
}
 
public static void main(string[] args) throws interruptedexception{
thread[] threads = new thread[10];
for(int i = 0;i<10;i++){
threads[i] = new thread(new plus());
threads[i].start();
}
for(int i = 0;i<10;i++){
threads[i].join();
}
system.out.println(i);
}

既然這樣,那怎么辦。沒錯,也許你已經想到了,可以加鎖或者利用 synchronized 實現,例如,將 add() 方法修改為如下這樣:

?
1
2
3
public synchronized static void add(){
 i++;
 }

或者,加鎖操作,例如下面使用 reentrantlock (可重入鎖)實現。

?
1
2
3
4
5
6
private static lock lock = new reentrantlock();
 public static void add(){
 lock.lock();
 i++;
 lock.unlock();
 }

cas 實現自旋鎖

既然用鎖或 synchronized 關鍵字可以實現原子操作,那么為什么還要用 cas 呢,因為加鎖或使用 synchronized 關鍵字帶來的性能損耗較大,而用 cas 可以實現樂觀鎖,它實際上是直接利用了 cpu 層面的指令,所以性能很高。

上面也說了,cas 是實現自旋鎖的基礎,cas 利用 cpu 指令保證了操作的原子性,以達到鎖的效果,至于自旋呢,看字面意思也很明白,自己旋轉,翻譯成人話就是循環,一般是用一個無限循環實現。這樣一來,一個無限循環中,執行一個 cas 操作,當操作成功,返回 true 時,循環結束;當返回 false 時,接著執行循環,繼續嘗試 cas 操作,直到返回 true。

其實 jdk 中有好多地方用到了 cas ,尤其是java.util.concurrent包下,比如 countdownlatch、semaphore、reentrantlock 中,再比如 java.util.concurrent.atomic 包下,相信大家都用到過 atomic* ,比如 atomicboolean、atomicinteger 等。

這里拿 atomicboolean 來舉個例子,因為它足夠簡單。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class atomicboolean implements java.io.serializable {
 private static final long serialversionuid = 4654671469794556979l;
 // setup to use unsafe.compareandswapint for updates
 private static final unsafe unsafe = unsafe.getunsafe();
 private static final long valueoffset;
 static {
 try {
 valueoffset = unsafe.objectfieldoffset
 (atomicboolean.class.getdeclaredfield("value"));
 } catch (exception ex) { throw new error(ex); }
 }
 private volatile int value;
 
 public final boolean get() {
 return value != 0;
 }
 public final boolean compareandset(boolean expect, boolean update) {
 int e = expect ? 1 : 0;
 int u = update ? 1 : 0;
 return unsafe.compareandswapint(this, valueoffset, e, u);
 }
}

這是 atomicboolean 的部分代碼,我們看到這里面又幾個關鍵方法和屬性。

1、使用了 sun.misc.unsafe 對象,這個類提供了一系列直接操作內存對象的方法,只是在 jdk 內部使用,不建議開發者使用;

2、value 表示實際值,可以看到 get 方法實際是根據 value 是否等于0來判斷布爾值的,這里的 value 定義為 volatile,因為 volatile 可以保證內存可見性,也就是 value 值只要發生變化,其他線程是馬上可以看到變化后的值的;下一篇會講一下  volatile 可見性問題,歡迎關注

3、valueoffset 是 value 值的內存偏移量,用 unsafe.objectfieldoffset 方法獲得,用作后面的 compareandset 方法;

4、compareandset 方法,這就是實現 cas 的核心方法了,在使用 atomicboolean 的這個方法時,只需要傳遞期望值和待更新的值即可,而它里面調用了 unsafe.compareandswapint(this, valueoffset, e, u) 方法,它是個 native 方法,用 c++ 實現,具體的代碼就不貼了,總之是利用了 cpu 的 cmpxchg 指令完成比較并替換,當然根據具體的系統版本不同,實現起來也有所區別,感興趣的可以自行搜一下相關文章。

使用場景

  • cas 適合簡單對象的操作,比如布爾值、整型值等;
  • cas 適合沖突較少的情況,如果太多線程在同時自旋,那么長時間循環會導致 cpu 開銷很大;

比如 atomicboolean 可以用在這樣一個場景下,系統需要根據一個布爾變量的狀態屬性來判斷是否需要執行一些初始化操作,如果是多線程的環境下,避免多次重復執行,可以使用 atomicboolean 來實現,偽代碼如下:

?
1
2
3
4
private final static atomicboolean flag = new atomicboolean();
 if(flag.compareandset(false,true)){
 init();
 }

比如 atomicinteger 可以用在計數器中,多線程環境中,保證計數準確。

aba問題

cas 存在一個問題,就是一個值從 a 變為 b ,又從 b 變回了 a,這種情況下,cas 會認為值沒有發生過變化,但實際上是有變化的。對此,并發包下倒是有 atomicstampedreference 提供了根據版本號判斷的實現,可以解決一部分問題。

總結

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對服務器之家的支持。

原文鏈接:https://mp.weixin.qq.com/s/VeHq-LFPTYbtO6DsHKwngw

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 欧美日韩亚洲高清不卡一区二区三区 | 成人曼画 | 欧美影院天天5g天天爽 | 不良研究所地址一 | 传说之下羊妈挤羊奶网站 | 91麻豆精东果冻天美传媒老狼 | www日本高清视频 | 日本妇人成熟免费观看18 | 欧美日韩亚洲高清不卡一区二区三区 | 国产欧美成人免费观看 | 扒开胸流出吃奶 | 亚洲日本在线观看网址 | 日韩欧美一区二区三区中文精品 | 国产精品久久久久这里只有精品 | 风间由美一区二区播放合集 | 国产码一区二区三区 | 欧美激情影音先锋 | 日本成人免费在线视频 | 国产1区2区在线观看 | 亚洲天堂男人 | 91女神在线观看 | 蜜桃成人影院 | 2022超帅男同gayxxx | 国内精品久久久久影院男同志 | 欧美高清在线精品一区二区不卡 | 99ri在线精品视频在线播放 | 亚洲国产精品成人综合久久久 | 青青草成人在线观看 | 亚洲欧洲日产国码无码av | 精品午夜寂寞影院在线观看 | 国产卡一卡二卡三卡四 | 欧美日韩中文国产一区二区三区 | 99视频在线看 | 日韩精品一区二区三区视频 | 四虎现在的网址入口2022 | 欧美影院天天5g天天爽 | 范冰冰好紧好滑好湿 | 亚洲成综合人影院在院播放 | 村上里沙40分钟在线观看 | 成年人在线观看视频免费 | 人与善xuanwen在线400 |