国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

「JUC并發(fā)編程」初識CAS鎖(概述、底層原理、原子引用、自旋鎖、缺點)

這篇具有很好參考價值的文章主要介紹了「JUC并發(fā)編程」初識CAS鎖(概述、底層原理、原子引用、自旋鎖、缺點)。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

一、什么是CAS鎖

概述

CAS的全稱為Compare-And-Swap,直譯就是對比交換。是一條CPU的原子指令,其作用是讓CPU進行比較兩個值是否相等,然后原子地更新某個位置的值。經(jīng)過調(diào)查發(fā)現(xiàn),其實現(xiàn)方式是基于硬件平臺的匯編指令,就是說CAS是靠硬件實現(xiàn)的,JVM只是封裝了匯編調(diào)用,那些AtomicInteger類便是使用了這些封裝后的接口。 簡單解釋:CAS操作需要輸入兩個數(shù)值,一個舊值(期望操作前的值)和一個新值,在操作期間先比較下在舊值有沒有發(fā)生變化,如果沒有發(fā)生變化,才交換成新值,發(fā)生了變化則不交換。

原理

CAS有3個操作數(shù),位置內(nèi)存值V,舊的預(yù)期值A(chǔ),要修改的更新值B。

當(dāng)且僅當(dāng)舊的預(yù)期值A(chǔ)和內(nèi)存值V相同時,將內(nèi)存值V修改為B,否則什么都不做或重來 。

cas鎖,JUC并發(fā)編程,jvm,java,算法

硬件級別保證

CAS是JDK提供的非阻塞原子性操作,它通過 硬件保證了比較-更新的原子性。

它是非阻塞的且自身原子性,也就是說這玩意效率更高且通過硬件保證,說明這玩意更可靠

示例代碼

public class CASDemo {
    public static void main(String[] args) {
        AtomicInteger atomicInteger = new AtomicInteger(5);
        //期望時5,如果是5則改成2022
        System.out.println(atomicInteger.compareAndSet(5, 2022) + "\t" + atomicInteger.get());
        System.out.println(atomicInteger.compareAndSet(5, 2022) + "\t" + atomicInteger.get());
    }
}

輸出結(jié)果

cas鎖,JUC并發(fā)編程,jvm,java,算法

由于前面修改了,后面修改失敗,故先true后false。

源碼分析compareAndSet(int expect,int update)

compareAndSet()方法的源代碼:

cas鎖,JUC并發(fā)編程,jvm,java,算法

上面三個方法都是類似的,主要對4個參數(shù)做一下說明。

var1:表示要操作的對象

var2:表示要操作對象中屬性地址的偏移量

var4:表示需要修改數(shù)據(jù)的期望的值

var5/var6:表示需要修改為的新值 cas鎖,JUC并發(fā)編程,jvm,java,算法

引出來一個問題:UnSafe類是什么?

二、CAS底層原理

Unsafe

unsafe是CAS的核心類,由于Java方法無法直接訪問底層系統(tǒng),需要通過本地(native)方法來訪問,Unsafe相當(dāng)于一個后門,基于該類可以直接操作特定內(nèi)存的數(shù)據(jù)。 Unsafe類存在于sun.misc包中 ,其內(nèi)部方法操作可以像C的指針 一樣直接操作內(nèi)存,因為Java中CAS操作的執(zhí)行依賴于Unsafe類的方法。

注意Unsafe類中的所有方法都是native修飾的,也就是說Unsafe類中的方法都直接調(diào)用操作系統(tǒng)底層資源執(zhí)行相應(yīng)任務(wù)

valueOffset

變量valueOffset,表示該變量值在內(nèi)存中的 偏移地址 ,因為Unsafe就是根據(jù)內(nèi)存偏移地址獲取數(shù)據(jù)的

volatile

變量value用volatile修飾,保證了多線程之間的內(nèi)存可見性。

源碼分析

OpenJDK源碼里面查看下 Unsafe.java

cas鎖,JUC并發(fā)編程,jvm,java,算法

假設(shè)線程A和線程B兩個線程同時執(zhí)行g(shù)etAndAddInt操作(分別跑在不同CPU上):

1 AtomicInteger里面的value原始值為3,即主內(nèi)存中AtomicInteger的value為3,根據(jù)JMM模型,線程A和線程B各自持有一份值為3的value的副本分別到各自的工作內(nèi)存。

2 線程A通過getIntVolatile(var1, var2)拿到value值3,這時線程A被掛起。

3 線程B也通過getIntVolatile(var1, var2)方法獲取到value值3,此時剛好線程B 沒有被掛起 并執(zhí)行compareAndSwapInt方法比較內(nèi)存值也為3,成功修改內(nèi)存值為4,線程B打完收工,一切OK。

4 這時線程A恢復(fù),執(zhí)行compareAndSwapInt方法比較,發(fā)現(xiàn)自己手里的值數(shù)字3和主內(nèi)存的值數(shù)字4不一致,說明該值已經(jīng)被其它線程搶先一步修改過了,那A線程本次修改失敗, 只能重新讀取重新來一遍了。

5 線程A重新獲取value值,因為變量value被volatile修飾,所以其它線程對它的修改,線程A總是能夠看到,線程A繼續(xù)執(zhí)行compareAndSwapInt進行比較替換,直到成功。

底層匯編

native修飾的方法代表是底層方法

Unsafe類中的compareAndSwapInt,是一個本地方法,該方法的實現(xiàn)位于unsafe.cpp中

UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSwapInt(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint e, jint x))
  UnsafeWrapper("Unsafe_CompareAndSwapInt"); 
  oop p = JNIHandles::resolve(obj); 
// 先想辦法拿到變量value在內(nèi)存中的地址,根據(jù)偏移量valueOffset,計算 value 的地址 
  jint* addr = (jint *) index_oop_from_field_offset_long(p, offset); 
// 調(diào)用 Atomic 中的函數(shù) cmpxchg來進行比較交換,其中參數(shù)x是即將更新的值,參數(shù)e是原內(nèi)存的值 
  return (jint) (Atomic::cmpxchg(x, addr, e)) == e; 
UNSAFE_END 

(Atomic::cmpxchg(x, addr, e)) == e; (主要源碼)

cmpxchg

調(diào)用 Atomic 中的函數(shù) cmpxchg來進行比較交換,其中參數(shù)x是即將更新的值,參數(shù)e是原內(nèi)存的值

return (jint) (Atomic::cmpxchg(x, addr, e)) == e;

unsigned Atomic:: cmpxchg (unsigned int exchange_value,volatile unsigned int* dest, unsigned int compare_value) {
    assert(sizeof(unsigned int) == sizeof(jint), "more work to do"); 
   /* 
   * 根據(jù)操作系統(tǒng)類型調(diào)用不同平臺下的重載函數(shù),這個在預(yù)編譯期間編譯器會決定調(diào)用哪個平臺下的重載函數(shù)*/ 
    return (unsigned int)Atomic::cmpxchg((jint)exchange_value, (volatile jint*)dest, (jint)compare_value); 
} 

總結(jié)

你只需要記?。?/p>

  • CAS是靠硬件實現(xiàn)的從而在硬件層面提升效率,最底層還是交給硬件來保證原子性和可見性
  • 實現(xiàn)方式是基于硬件平臺的匯編指令,在intel的CPU中(X86機器上),使用的是匯編指令cmpxchg指令。

核心思想就是:比較要更新變量的值V和預(yù)期值E(compare),相等才會將V的值設(shè)為新值N(swap)如果不相等自旋再來。

三、原子引用

在上面我們知道AtomicInteger原子整型,那可否有其它原子類型呢?

比如說:AtomicBook、AtomicOrder

答案是肯定的。這里引入AtomicReference

AtomicReference示例

@Data
@AllArgsConstructor
class User{
    String username;
    int age;
}
public class AtomicReferenceDemo {
    public static void main(String[] args) {
        User z3 = new User("z3", 24);
        User li4 = new User("li4", 26);

        AtomicReference<User> atomicReferenceUser = new AtomicReference<>();

        atomicReferenceUser.set(z3);
        System.out.println(atomicReferenceUser.compareAndSet(z3, li4) + "\t" + atomicReferenceUser.get().toString());
        System.out.println(atomicReferenceUser.compareAndSet(z3, li4) + "\t" + atomicReferenceUser.get().toString());
    }
}

四、自旋鎖,借鑒CAS思想

什么是自旋鎖?

自旋鎖是指嘗試獲取鎖的線程不會立即阻塞,而是采用循環(huán)的方式 去嘗試獲取鎖 ,

當(dāng)線程發(fā)現(xiàn)鎖被占用時,會不斷循環(huán)判斷鎖的狀態(tài),直到獲取。這樣的好處是減少線程上下文切換的消耗,缺點是循環(huán)會消耗CPU 。

示例

題目:實現(xiàn)一個自旋鎖
自旋鎖好處:循環(huán)比較獲取沒有類似 wait 的阻塞。
通過 CAS 操作完成自旋鎖。

public class SpinLockDemo {
    AtomicReference<Thread> atomicReference = new AtomicReference<>();

    public void lock() {
        Thread thread = Thread.currentThread();
        System.out.println(Thread.currentThread().getName() + "\t" + "---come in");
        while (!atomicReference.compareAndSet(null, thread)) {

        }
    }

    public void unlock() {
        Thread thread = Thread.currentThread();
        atomicReference.compareAndSet(thread, null);
        System.out.println(Thread.currentThread().getName() + "\t" + "---task over , unlock ...");
    }

    public static void main(String[] args) {
        SpinLockDemo spinLockDemo = new SpinLockDemo();
        new Thread(() -> {
            spinLockDemo.lock();
            try {
                TimeUnit.MILLISECONDS.sleep(5000);
            } catch (Exception e) {
                e.printStackTrace();
            }
            spinLockDemo.unlock();
        }, "A").start();

        new Thread(() -> {
            spinLockDemo.lock();

            spinLockDemo.unlock();
        }, "B").start();

    }
}

輸出結(jié)果

A線程先進,B線程后進。緊接著A線程等待,然后解鎖,B線程在A線程解鎖后才會解鎖。

cas鎖,JUC并發(fā)編程,jvm,java,算法

解析

A 線程先進來調(diào)用 myLock 方法自己持有鎖 5 秒鐘, B 隨后進來后發(fā)現(xiàn)
當(dāng)前有線程持有鎖,不是 null ,所以只能通過自旋等待,直到 A 釋放鎖后 B 隨后搶到。

這種自旋等待嘗試的過程就是自旋鎖。

五、CAS的缺點

循環(huán)時間長開銷很大

我們可以看到getAndAddInt方法執(zhí)行時,有個do while 。

cas鎖,JUC并發(fā)編程,jvm,java,算法

如果CAS失敗,會一直進行嘗試。如果CAS長時間一直不成功,可能會給CPU帶來很大的開銷。

引出來ABA問題

ABA問題怎么產(chǎn)生的

CAS算法實現(xiàn)一個重要前提需要取出內(nèi)存中某時刻的數(shù)據(jù)并在當(dāng)下時刻比較并替換,那么在這個時間差類會導(dǎo)致數(shù)據(jù)的變化。

比如說一個線程one從內(nèi)存位置V中取出A,這時候另一個線程two也從內(nèi)存中取出A,并且線程two進行了一些操作將值變成了B,

然后線程two又將V位置的數(shù)據(jù)變成A,這時候線程one進行CAS操作發(fā)現(xiàn)內(nèi)存中仍然是A,然后線程one操作成功。

盡管線程one的CAS操作成功,但是不代表這個過程就是沒有問題的。文章來源地址http://www.zghlxwxcb.cn/news/detail-594923.html

到了這里,關(guān)于「JUC并發(fā)編程」初識CAS鎖(概述、底層原理、原子引用、自旋鎖、缺點)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費用

相關(guān)文章

  • 并發(fā)編程-JUC-原子類

    并發(fā)編程-JUC-原子類

    JUC 整體概覽 原子類 基本類型-使用原子的方式更新基本類型 AtomicInteger:整形原子類 AtomicLong:長整型原子類 AtomicBoolean :布爾型原子類 引用類型 AtomicReference:引用類型原子類 AtomicStampedReference:原子更新引用類型里的字段原子類 AtomicMarkableReference :原子更新帶有標(biāo)記位的引

    2024年02月21日
    瀏覽(23)
  • JUC并發(fā)編程之原子類

    目錄 1. 什么是原子操作 1.1?原子類的作用 1.2?原子類的常見操作 原子類的使用注意事項 并發(fā)編程是現(xiàn)代計算機應(yīng)用中不可或缺的一部分,而在并發(fā)編程中,處理共享資源的并發(fā)訪問是一個重要的問題。為了避免多線程訪問共享資源時出現(xiàn)競態(tài)條件(Race Condition)等問題,J

    2024年02月13日
    瀏覽(18)
  • JUC并發(fā)編程學(xué)習(xí)筆記(十九)原子引用

    帶版本號的原子操作! 解決ABA問題,引入原子引用(樂觀鎖思想) AtomicStampedReference類解決ABA問題 所有相同類型的包裝類對象之間值的比較全部使用equals方法比較 Integer使用了對象緩存機制,默認(rèn)范圍是-128至127,推薦使用靜態(tài)工廠方法valueOf獲取對象實例,而不是new,因為v

    2024年02月05日
    瀏覽(22)
  • 【Java 并發(fā)編程】CAS 原理解析

    【Java 并發(fā)編程】CAS 原理解析

    悲觀鎖 的原理是每次實現(xiàn)數(shù)據(jù)庫的增刪改的時候都進?阻塞,防?數(shù)據(jù)發(fā)?臟讀。 樂觀鎖 的原理是在數(shù)據(jù)庫更新的時候,??個 version 字段來記錄版本號,然后通過?較是不是??要修改的版本號再進?修改。這其中就引出了?種?較交換的思路來實現(xiàn)數(shù)據(jù)的?致性,事實

    2024年02月06日
    瀏覽(25)
  • JUC并發(fā)編程原理精講(源碼分析)

    JUC并發(fā)編程原理精講(源碼分析)

    JUC即 java.util.concurrent 涉及三個包: java.util.concurrent java.util.concurrent.atomic java.util.concurrent.locks 普通的線程代碼: Thread Runnable 沒有返回值、效率相比入 Callable 相對較低! Callable 有返回值!【工作常用】 進程 :是指一個內(nèi)存中運行的程序,每個進程都有一個獨立的內(nèi)存空間,

    2024年02月02日
    瀏覽(27)
  • JUC并發(fā)編程之AQS原理

    JUC并發(fā)編程之AQS原理

    全稱是 AbstractQueuedSynchronizer,是阻塞式鎖和相關(guān)的同步器工具的框架 特點: 用 state 屬性來表示資源的狀態(tài)(分獨占模式和共享模式),子類需要定義如何維護這個生態(tài),控制如何獲取鎖和釋放鎖 getState - 獲取 state 狀態(tài) setState - 設(shè)置 state 狀態(tài) compareAndSetState - cas 機制設(shè)置 s

    2023年04月18日
    瀏覽(30)
  • 【并發(fā)編程】JUC并發(fā)編程(徹底搞懂JUC)

    【并發(fā)編程】JUC并發(fā)編程(徹底搞懂JUC)

    如果你對多線程沒什么了解,那么從入門模塊開始。 如果你已經(jīng)入門了多線程(知道基礎(chǔ)的線程創(chuàng)建、死鎖、synchronized、lock等),那么從juc模塊開始。 新手學(xué)技術(shù)、老手學(xué)體系,高手學(xué)格局。 JUC實際上就是我們對于jdk中java.util .concurrent 工具包的簡稱 ,其結(jié)構(gòu)如下: 這個包

    2024年02月20日
    瀏覽(19)
  • Java并發(fā)編程挑戰(zhàn)與解決方案:上下文切換、死鎖、資源限制及底層實現(xiàn)原理

    Java并發(fā)編程挑戰(zhàn)與解決方案:上下文切換、死鎖、資源限制及底層實現(xiàn)原理

    深入探討Java并發(fā)編程中的挑戰(zhàn),包括上下文切換、死鎖、資源限制,并介紹解決方案如減少上下文切換、避免死鎖等。了解Java并發(fā)機制的底層實現(xiàn)原理和線程間通信方法。

    2024年02月01日
    瀏覽(33)
  • 【并發(fā)編程】CAS到底是什么

    Java實現(xiàn)CAS的原理 | Java程序員進階之路 美團終面:CAS確定完全不需要鎖嗎? CAS 是 Compare-And-Swap (比較并交換)的縮寫,是一種 輕量級的同步機制 ,主要用于實現(xiàn)多線程環(huán)境下的無鎖算法和數(shù)據(jù)結(jié)構(gòu),保證了并發(fā)安全性。它可以在 不使用鎖 (如synchronized、Lock)的情況下,對

    2024年02月20日
    瀏覽(23)
  • JUC并發(fā)編程(二)

    JUC并發(fā)編程(二)

    JUC并發(fā)編程(續(xù)) 接上一篇筆記:https://blog.csdn.net/weixin_44780078/article/details/130694996 五、Java內(nèi)存模型 JMM 即 Java Memory Model,它定義了主存、工作內(nèi)存抽象概念,底層對應(yīng)著CPU寄存器、緩存、硬件內(nèi)存、CPU 指令優(yōu)化等。 JMM 體現(xiàn)在以下幾個方面: 原子性:保證指令不會受到線程

    2024年02月05日
    瀏覽(27)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包