日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

CAS和AQS

發(fā)布時間:2023/11/30 编程问答 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 CAS和AQS 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

CAS

全稱(Compare And Swap),比較交換

Unsafe類是CAS的核心類,提供硬件級別的原子操作

?

// 對象、對象的地址、預(yù)期值、修改值 public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);

缺點(diǎn):

  • 開銷大:在并發(fā)量比較高的情況下,如果反復(fù)嘗試更新某個變量,卻又一直更新不成功,會給CPU帶來較大的壓力
  • ABA問題:當(dāng)變量從A修改為B在修改回A時,變量值等于期望值A(chǔ),但是無法判斷是否修改,CAS操作在ABA修改后依然成功。
    • 如何避免:Java提供了AtomicStampedReference和AtomicMarkableReference來解決。AtomicStampedReference通過包裝[E,Integer]的元組來對對象標(biāo)記版本戳stamp,對于ABA問題其解決方案是加上版本號,即在每個變量都加上一個版本號,每次改變時加1,即A —> B —> A,變成1A —> 2B —> 3A。
  • 不能保證代碼塊的原子性:CAS機(jī)制所保證的只是一個變量的原子性操作,而不能保證整個代碼塊的原子性。
  • ?

    public class Test {private static AtomicInteger atomicInteger = new AtomicInteger(100);private static AtomicStampedReference atomicStampedReference = new AtomicStampedReference(100,1);public static void main(String[] args) throws InterruptedException {//AtomicIntegerThread at1 = new Thread(new Runnable() {@Overridepublic void run() {atomicInteger.compareAndSet(100,110);atomicInteger.compareAndSet(110,100);}});Thread at2 = new Thread(new Runnable() {@Overridepublic void run() {try {TimeUnit.SECONDS.sleep(2); // at1,執(zhí)行完} catch (InterruptedException e) {e.printStackTrace();}System.out.println("AtomicInteger:" + atomicInteger.compareAndSet(100,120));}});at1.start();at2.start();at1.join();at2.join();//AtomicStampedReferenceThread tsf1 = new Thread(new Runnable() {@Overridepublic void run() {try {//讓 tsf2先獲取stamp,導(dǎo)致預(yù)期時間戳不一致TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}// 預(yù)期引用:100,更新后的引用:110,預(yù)期標(biāo)識getStamp() 更新后的標(biāo)識getStamp() + 1atomicStampedReference.compareAndSet(100,110,atomicStampedReference.getStamp(),atomicStampedReference.getStamp() + 1);atomicStampedReference.compareAndSet(110,100,atomicStampedReference.getStamp(),atomicStampedReference.getStamp() + 1);}});Thread tsf2 = new Thread(new Runnable() {@Overridepublic void run() {int stamp = atomicStampedReference.getStamp();try {TimeUnit.SECONDS.sleep(2); //線程tsf1執(zhí)行完} catch (InterruptedException e) {e.printStackTrace();}System.out.println("AtomicStampedReference:" +atomicStampedReference.compareAndSet(100,120,stamp,stamp + 1));}});tsf1.start();tsf2.start();}}

    AQS(AbstractQueuedSynchronizer)

    維護(hù)一個volatile int state(代表共享資源狀態(tài))和一個FIFO線程等待隊(duì)列。

    模板方法基本分為三類:

    • 獨(dú)占鎖
    • 共享鎖
    • 釋放鎖

    資源共享的方式

  • Exclusive(獨(dú)占,只有一個線程能執(zhí)行,如ReentrantLock)
  • Share(共享,多個線程可以同時執(zhí)行,如Semaphore/CountDownLatch)
  • 同步隊(duì)列

    AQS依靠同步隊(duì)列(一個FIFO的雙向隊(duì)列)來完成同步狀態(tài)的管理。當(dāng)當(dāng)前線程獲取狀態(tài)失敗后,同步器會將當(dāng)前線程以及等待信息構(gòu)造成一個節(jié)點(diǎn)(Node),并嘗試將他加入到同步隊(duì)列。Head節(jié)點(diǎn)不保存等待的線程信息,僅通過next指向隊(duì)列中第一個保存等待線程信息的Node。

    ?

    雙向同步隊(duì)列

    Node類

    源碼(中字注釋)

    ?

    static final class Node {/** 代表共享模式 */static final Node SHARED = new Node();/** 代表獨(dú)占模式 */static final Node EXCLUSIVE = null;/** 以下四個狀態(tài)解釋見下文等待狀態(tài) */static final int CANCELLED = 1;static final int SIGNAL = -1;static final int CONDITION = -2;static final int PROPAGATE = -3;/** 標(biāo)識等待狀態(tài),通過CAS操作更新,原子操作不會被打斷*/volatile int waitStatus;/** 當(dāng)前節(jié)點(diǎn)的前置節(jié)點(diǎn) */volatile Node prev;/** 當(dāng)前節(jié)點(diǎn)的后置節(jié)點(diǎn) */volatile Node next;/** 該節(jié)點(diǎn)關(guān)聯(lián)的線程(未能獲取鎖,進(jìn)入等待的線程) */volatile Thread thread;/** 指向下一個在某個條件上等待的節(jié)點(diǎn),或者指向 SHARE 節(jié)點(diǎn),表明當(dāng)前處于共享模式*/Node nextWaiter;/*** 判斷是否處于共享模式*/final boolean isShared() {return nextWaiter == SHARED;}/** * 返回當(dāng)前節(jié)點(diǎn)的前置節(jié)點(diǎn) * 會做對前置節(jié)點(diǎn)空值判斷 */final Node predecessor() throws NullPointerException {Node p = prev;if (p == null)throw new NullPointerException();elsereturn p;}Node() { // Used to establish initial head or SHARED marker}Node(Thread thread, Node mode) { // Used by addWaiterthis.nextWaiter = mode;this.thread = thread;}Node(Thread thread, int waitStatus) { // Used by Conditionthis.waitStatus = waitStatus;this.thread = thread;} }

    等待狀態(tài):

    等待狀態(tài)的修改是CAS原子操作

    • CANCELED: 1,因?yàn)榈却瑫r (timeout)或者中斷(interrupt),節(jié)點(diǎn)會被置為取消狀態(tài)。處于取消狀態(tài)的節(jié)點(diǎn)不會再去競爭鎖,也就是說不會再被阻塞。節(jié)點(diǎn)會一直保持取消狀態(tài),而不會轉(zhuǎn)換為其他狀態(tài)。處于 CANCELED 的節(jié)點(diǎn)會被移出隊(duì)列,被 GC 回收。
    • SIGNAL: -1,表明當(dāng)前的后繼結(jié)點(diǎn)正在或者將要被阻塞(通過使用 LockSupport.pack 方法),因此當(dāng)前的節(jié)點(diǎn)被釋放(release)或者被取消時(cancel)時,要喚醒它的后繼結(jié)點(diǎn)(通過 LockSupport.unpark 方法)。
    • CONDITION: -2,表明當(dāng)前節(jié)點(diǎn)在條件隊(duì)列中,因?yàn)榈却硞€條件而被阻塞。
    • PROPAGATE: -3,在共享模式下,可以認(rèn)為資源有多個,因此當(dāng)前線程被喚醒之后,可能還有剩余的資源可以喚醒其他線程。該狀態(tài)用來表明后續(xù)節(jié)點(diǎn)會傳播喚醒的操作。需要注意的是只有頭節(jié)點(diǎn)才可以設(shè)置為該狀態(tài)(This is set (for head node only) in doReleaseShared to ensure propagation continues, even if other operations have since intervened.)。
    • 0:新創(chuàng)建的節(jié)點(diǎn)會處于這種狀態(tài)

    鎖的獲取與釋放:

    獲取獨(dú)占鎖

    ?

    獲取獨(dú)占鎖

    • acquire方法

    ?

    public final void acquire(int arg) {// 首先嘗試獲取鎖,如果獲取失敗,會先調(diào)用 addWaiter 方法創(chuàng)建節(jié)點(diǎn)并追加到隊(duì)列尾部// 然后調(diào)用 acquireQueued 阻塞或者循環(huán)嘗試獲取鎖if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)){// 在 acquireQueued 中,如果線程是因?yàn)橹袛喽顺龅淖枞麪顟B(tài)會返回 true// 這里的 selfInterrupt 主要是為了恢復(fù)線程的中斷狀態(tài)selfInterrupt();} }

    釋放獨(dú)占鎖

    ?

    釋放獨(dú)占鎖

    ? 在獨(dú)占模式中,鎖的釋放由于沒有其他線程競爭,相對簡單。鎖釋放失敗的原因是由于該線程本身不擁有鎖,而非多線程競爭。鎖釋放成功后會檢查后置節(jié)點(diǎn)的狀態(tài),找到合適的節(jié)點(diǎn),調(diào)用unparkSuccessor方法喚醒該節(jié)點(diǎn)所關(guān)聯(lián)的線程。

    • release方法

    ?

    public final boolean release(int arg) {if (tryRelease(arg)) {Node h = head;// waitStatus 為 0,證明是初始化的空隊(duì)列或者后繼結(jié)點(diǎn)已經(jīng)被喚醒了if (h != null && h.waitStatus != 0)unparkSuccessor(h);return true;}return false; }

    獲取共享鎖

    ?

    獲取共享鎖

    • acquireShared方法

      通過該方法可以申請鎖

    ?

    public final void acquireShared(int arg) {// 如果返回結(jié)果小于0,證明沒有獲取到共享資源if (tryAcquireShared(arg) < 0)doAcquireShared(arg); }
    • doAcquireShared

    ?

    private void doAcquireShared(int arg) {final Node node = addWaiter(Node.SHARED);boolean failed = true;try {boolean interrupted = false;for (;;) {final Node p = node.predecessor();if (p == head) {int r = tryAcquireShared(arg);if (r >= 0) {setHeadAndPropagate(node, r);p.next = null; // help GCif (interrupted)selfInterrupt();failed = false;return;}}if (shouldParkAfterFailedAcquire(p, node) &&parkAndCheckInterrupt())interrupted = true;}} finally {if (failed)cancelAcquire(node);} }

    釋放共享鎖



    作者:RealityVibe
    鏈接:https://www.jianshu.com/p/2a48778871a9
    來源:簡書
    著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請注明出處。

    創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎

    總結(jié)

    以上是生活随笔為你收集整理的CAS和AQS的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。