久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

并發(fā)策略-CAS算法

 WindySky 2018-03-09

  對(duì)于并發(fā)控制而言,我們平時(shí)用的鎖(synchronized,,Lock)是一種悲觀的策略,。它總是假設(shè)每一次臨界區(qū)操作會(huì)產(chǎn)生沖突,因此,,必須對(duì)每次操作都小心翼翼,。如果多個(gè)線程同時(shí)訪問臨界區(qū)資源,就寧可犧牲性能讓線程進(jìn)行等待,,所以鎖會(huì)阻塞線程執(zhí)行,。

  與之相對(duì)的有一種樂觀的策略,它會(huì)假設(shè)對(duì)資源的訪問是沒有沖突的,。既然沒有沖突也就無需等待了,,所有的線程都在不停頓的狀態(tài)下持續(xù)執(zhí)行。那如果遇到問題了無鎖的策略使用一種叫做比較交換(CAS Compare And Swap)來鑒別線程沖突,,一旦檢測(cè)到?jīng)_突產(chǎn)生,,就重試當(dāng)前操作直到?jīng)]有沖突。CAS算法是非阻塞的,,它對(duì)死鎖問題天生免疫,,而且它比基于鎖的方式擁有更優(yōu)越的性能。

  CAS算法的過程是這樣:它包含三個(gè)參數(shù) CAS(V,E,N),。V表示要更新的變量,,E表示預(yù)期的值,N表示新值,。僅當(dāng)V值等于E值時(shí),,才會(huì)將V的值設(shè)置成N,否則什么都不做,。最后CAS返回當(dāng)前V的值,。CAS算法需要你額外給出一個(gè)期望值,也就是你認(rèn)為現(xiàn)在變量應(yīng)該是什么樣子,,如果變量不是你想象的那樣,,那說明已經(jīng)被別人修改過,。你就重新讀取,再次嘗試修改即可,。

  JDK并發(fā)包有一個(gè)atomic包,,里面實(shí)現(xiàn)了一些直接使用CAS操作的線程安全的類型。其中最常用的一個(gè)類應(yīng)該就是AtomicInteger,。我們以此為例來研究一下沒有鎖的情況下如何做到線程安全,。

private volatile int value;

  這是AtomicInteger類的核心字段,,代表當(dāng)前實(shí)際取值,,借助volatile保證線程間數(shù)據(jù)的可見性,。

  獲取內(nèi)部數(shù)據(jù)的方法:

  public final int get() {
        return value;
    }

  我們關(guān)注一下incrementAndGet()的內(nèi)部實(shí)現(xiàn)  

復(fù)制代碼
1  public final int incrementAndGet() {
2         for (;;) {
3             int current = get();
4             int next = current + 1;
5             if (compareAndSet(current, next))
6                 return next;
7         }
8     }
復(fù)制代碼

 

  代碼第二行使用了一個(gè)死循環(huán),,原因是:CAS的操作未必都是成功的,因此對(duì)于不成功的情況,,我們就需要進(jìn)行不斷的嘗試,。第三行取得當(dāng)前值,,接著+1得到新值next,。這里我們使用CAS必需的兩個(gè)參數(shù):期望值以及新值,。使用compareAndSet()將新值next寫入,。成功的條件是在寫入的時(shí)刻,,當(dāng)前的值應(yīng)該要等于剛剛?cè)〉降腸urrent。如果不是這樣則說明AtomicInteger的值在第3行到第5行之間被其他線程修改過了,。當(dāng)前看到的狀態(tài)是一個(gè)過期的狀態(tài),,因此返回失敗,需要進(jìn)行下一次重試,,知道成功為止,。

    public final boolean compareAndSet(int expect, int update) {
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }

  整體的過程就是這樣子的,利用CPU的CAS指令,,同時(shí)借助JNI來完成Java的非阻塞算法,。其它原子操作都是利用類似的特性完成的。大概的邏輯應(yīng)該是這樣:

if (this == expect) {
    this = update
    return true;
} else {
    return false;
} 

  CAS雖然能高效的解決原子問題,,但是CAS也會(huì)帶來1個(gè)經(jīng)典問題即ABA問題:

  因?yàn)镃AS需要在操作值的時(shí)候檢查下值有沒有發(fā)生變化,,如果沒有發(fā)生變化則更新,但是如果一個(gè)值原來是A,,變成了B,,又變成了A,那么使用CAS進(jìn)行檢查時(shí)會(huì)發(fā)現(xiàn)它的值沒有發(fā)生變化,,但是實(shí)際上卻變化了,。

  ABA問題的解決思路就是使用版本號(hào)。在變量前面追加上版本號(hào),,每次變量更新的時(shí)候把版本號(hào)加一,,那么A-B-A 就會(huì)變成1A-2B-3A。

  從Java1.5開始JDK的atomic包里提供了一個(gè)類AtomicStampedReference來解決ABA問題。這個(gè)類在內(nèi)部不僅維護(hù)了對(duì)象值,,還維護(hù)了一個(gè)時(shí)間戳(可以是任意的一個(gè)整數(shù)來表示狀態(tài)值),。當(dāng)設(shè)置對(duì)象值時(shí),對(duì)象值和狀態(tài)值都必須滿足期望值才會(huì)寫入成功,。因此即使對(duì)象被反復(fù)讀寫,,寫會(huì)原值,只要狀態(tài)值發(fā)生變化,,就能防止不恰當(dāng)?shù)膶懭?。 ?/p>

復(fù)制代碼
/**
* @param expectedReference 期望值
* @param newReference 寫入新值
* @param expectedStamp 期望狀態(tài)值
* @param newStamp 新狀態(tài)值
* @return true if successful
*/

public
boolean compareAndSet(V expectedReference, V newReference, int expectedStamp, int newStamp) { Pair<V> current = pair; return expectedReference == current.reference && expectedStamp == current.stamp && ((newReference == current.reference && newStamp == current.stamp) || casPair(current, Pair.of(newReference, newStamp))); }
復(fù)制代碼

 

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,,所有內(nèi)容均由用戶發(fā)布,,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式,、誘導(dǎo)購(gòu)買等信息,,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,,請(qǐng)點(diǎn)擊一鍵舉報(bào),。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多