欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java多線程并發(fā)Unsafe與CAS的用法解讀

 更新時間:2025年05月16日 10:32:52   作者:二六八  
這篇文章主要介紹了Java多線程并發(fā)Unsafe與CAS的用法,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教

Java多線程并發(fā) Unsafe與CAS的理解

1 Unsafe是什么

Java無法直接訪問底層操作系統(tǒng),而是通過本地(native)方法來訪問。不過盡管如此,JVM還是開了一個后門,JDK中有一個類Unsafe,它提供了硬件級別的原子操作。

這個類盡管里面的方法都是public的,但是并沒有辦法使用它們,JDK API文檔也沒有提供任何關于這個類的方法的解釋??偠灾?,對于Unsafe類的使用都是受限制的,只有授信的代碼才能獲得該類的實例,當然JDK庫里面的類是可以隨意使用的。

從第一行的描述可以了解到Unsafe提供了硬件級別的操作,比如說獲取某個屬性在內(nèi)存中的位置,比如說修改對象的字段值,即使它是私有的。不過Java本身就是為了屏蔽底層的差異,對于一般的開發(fā)而言也很少會有這樣的需求。

public native long staticFieldOffset(Field paramField);

這個方法可以用來獲取給定的paramField的內(nèi)存地址偏移量,這個值對于給定的field是唯一的且是固定不變的。

再比如說:

public native int arrayBaseOffset(Class paramClass);
public native int arrayIndexScale(Class paramClass);

前一個方法是用來獲取數(shù)組第一個元素的偏移地址,后一個方法是用來獲取數(shù)組的轉(zhuǎn)換因子即數(shù)組中元素的增量地址的。

最后看三個方法:

public native long allocateMemory(long paramLong);
public native long reallocateMemory(long paramLong1, long paramLong2);
public native void freeMemory(long paramLong);

分別用來分配內(nèi)存,擴充內(nèi)存和釋放內(nèi)存的。

當然這需要有一定的C/C++基礎,對內(nèi)存分配有一定的了解,這也是為什么我一直認為C/C++開發(fā)者轉(zhuǎn)行做Java會有優(yōu)勢的原因。

2 CAS是什么

CAS,Compare and Swap即比較并交換,設計并發(fā)算法時常用到的一種技術,java.util.concurrent包全完建立在CAS之上,沒有CAS也就沒有此包,可見CAS的重要性。

當前的處理器基本都支持CAS,只不過不同的廠家的實現(xiàn)不一樣罷了。CAS有三個操作數(shù):內(nèi)存值V、舊的預期值A、要修改的值B,當且僅當預期值A和內(nèi)存值V相同時,將內(nèi)存值修改為B并返回true,否則什么都不做并返回false。

CAS也是通過Unsafe實現(xiàn)的,看下Unsafe下的三個方法:

public final native boolean compareAndSwapObject(Object paramObject1, long paramLong, Object paramObject2, Object paramObject3);
public final native boolean compareAndSwapInt(Object paramObject, long paramLong, int paramInt1, int paramInt2);
public final native boolean compareAndSwapLong(Object paramObject, long paramLong1, long paramLong2, long paramLong3);

就拿中間這個比較并交換Int值為例好了,如果我們不用CAS,那么代碼大致是這樣的:

public int i = 1;
    
public boolean compareAndSwapInt(int j)
{
    if (i == 1)
    {
        i = j;
        return true;
    }
    return false;
}

當然這段代碼在并發(fā)下是肯定有問題的,有可能線程1運行到了第5行正準備運行第7行,線程2運行了,把i修改為10,線程切換回去,線程1由于先前已經(jīng)滿足第5行的if了,所以導致兩個線程同時修改了變量i。

解決辦法也很簡單,給compareAndSwapInt方法加鎖同步就行了,這樣,compareAndSwapInt方法就變成了一個原子操作。CAS也是一樣的道理,比較、交換也是一組原子操作,不會被外部打斷,先根據(jù)paramLong/paramLong1獲取到內(nèi)存當中當前的內(nèi)存值V,在將內(nèi)存值V和原值A作比較,要是相等就修改為要修改的值B,由于CAS都是硬件級別的操作,因此效率會高一些。

3 由CAS分析AtomicInteger原理

java.util.concurrent.atomic包下的原子操作類都是基于CAS實現(xiàn)的,下面拿AtomicInteger分析一下,首先是AtomicInteger類變量的定義:

private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;

static {
 try {
    valueOffset = unsafe.objectFieldOffset
        (AtomicInteger.class.getDeclaredField("value"));
  } catch (Exception ex) { throw new Error(ex); }
}

private volatile int value;

關于這段代碼中出現(xiàn)的幾個成員屬性:

  • 1、Unsafe是CAS的核心類,前面已經(jīng)講過了
  • 2、valueOffset表示的是變量值在內(nèi)存中的偏移地址,因為Unsafe就是根據(jù)內(nèi)存偏移地址獲取數(shù)據(jù)的原值的
  • 3、value是用volatile修飾的,這是非常關鍵的

下面找一個方法getAndIncrement來研究一下AtomicInteger是如何實現(xiàn)的,比如我們常用的addAndGet方法:

public final int addAndGet(int delta) {
    for (;;) {
        int current = get();
        int next = current + delta;
        if (compareAndSet(current, next))
            return next;
    }
}
 public final int get() {
  return value;
 }

這段代碼如何在不加鎖的情況下通過CAS實現(xiàn)線程安全,我們不妨考慮一下方法的執(zhí)行:

  • 1、AtomicInteger里面的value原始值為3,即主內(nèi)存中AtomicInteger的value為3,根據(jù)Java內(nèi)存模型,線程1和線程2各自持有一份value的副本,值為3
  • 2、線程1運行到第三行獲取到當前的value為3,線程切換
  • 3、線程2開始運行,獲取到value為3,利用CAS對比內(nèi)存中的值也為3,比較成功,修改內(nèi)存,此時內(nèi)存中的value改變比方說是4,線程切換
  • 4、線程1恢復運行,利用CAS比較發(fā)現(xiàn)自己的value為3,內(nèi)存中的value為4,得到一個重要的結(jié)論–>此時value正在被另外一個線程修改,所以我不能去修改它
  • 5、線程1的compareAndSet失敗,循環(huán)判斷,因為value是volatile修飾的,所以它具備可見性的特性,線程2對于value的改變能被線程1看到,只要線程1發(fā)現(xiàn)當前獲取的value是4,內(nèi)存中的value也是4,說明線程2對于value的修改已經(jīng)完畢并且線程1可以嘗試去修改它
  • 6、最后說一點,比如說此時線程3也準備修改value了,沒關系,因為比較-交換是一個原子操作不可被打斷,線程3修改了value,線程1進行compareAndSet的時候必然返回的false,這樣線程1會繼續(xù)循環(huán)去獲取最新的value并進行compareAndSet,直至獲取的value和內(nèi)存中的value一致為止

整個過程中,利用CAS機制保證了對于value的修改的線程安全性。

4 CAS的缺點

CAS看起來很美,但這種操作顯然無法涵蓋并發(fā)下的所有場景,并且CAS從語義上來說也不是完美的,存在這樣一個邏輯漏洞:如果一個變量V初次讀取的時候是A值,并且在準備賦值的時候檢查到它仍然是A值,那我們就能說明它的值沒有被其他線程修改過了嗎?

如果在這段期間它的值曾經(jīng)被改成了B,然后又改回A,那CAS操作就會誤認為它從來沒有被修改過。這個漏洞稱為CAS操作的"ABA"問題。

java.util.concurrent包為了解決這個問題,提供了一個帶有標記的原子引用類 AtomicStampedReference ,它可以通過控制變量值的版本來保證CAS的正確性。

不過目前來說這個類比較"雞肋",大部分情況下ABA問題并不會影響程序并發(fā)的正確性,如果需要解決ABA問題,使用傳統(tǒng)的互斥同步可能回避原子類更加高效。

總結(jié)

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關文章

  • java定位死鎖的三種方法(jstack、Arthas和Jvisualvm)

    java定位死鎖的三種方法(jstack、Arthas和Jvisualvm)

    這篇文章主要給大家介紹了關于java定位死鎖的三種方法,分別是通過jstack定位死鎖信息、通過Arthas工具定位死鎖以及通過 Jvisualvm 定位死鎖,文中還介紹了死鎖的預防方法,需要的朋友可以參考下
    2021-09-09
  • Java通俗易懂系列設計模式之模板模式

    Java通俗易懂系列設計模式之模板模式

    這篇文章主要介紹了Java通俗易懂系列設計模式之模板模式,想了解設計模式的同學,可以仔細看一下
    2021-04-04
  • Java如何不解壓讀取.zip的文件內(nèi)容

    Java如何不解壓讀取.zip的文件內(nèi)容

    這篇文章主要給大家介紹了關于Java如何不解壓讀取.zip的文件內(nèi)容的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2021-03-03
  • SpringBoot定時任務設計之時間輪案例原理詳解

    SpringBoot定時任務設計之時間輪案例原理詳解

    這篇文章主要為大家介紹了SpringBoot定時任務設計之時間輪案例原理詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-10-10
  • 解決MyEclipse6.5無法啟動,一直停留剛開始啟動界面的詳解

    解決MyEclipse6.5無法啟動,一直停留剛開始啟動界面的詳解

    本篇文章是對解決MyEclipse6.5無法啟動,一直停留剛開始啟動界面的方法進行了詳細的分析介紹,需要的朋友參考下
    2013-05-05
  • spring+springmvc+mybatis+maven入門實戰(zhàn)(超詳細教程)

    spring+springmvc+mybatis+maven入門實戰(zhàn)(超詳細教程)

    這篇文章主要介紹了spring+springmvc+mybatis+maven入門實戰(zhàn)(超詳細教程),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2019-05-05
  • 詳解Spring不同數(shù)據(jù)庫異常如何抽象的

    詳解Spring不同數(shù)據(jù)庫異常如何抽象的

    根據(jù)spring-jdbc中的定義,所有的數(shù)據(jù)操作異常都會轉(zhuǎn)換為 DataAccessException,下面這篇文章主要給大家介紹了關于Spring不同數(shù)據(jù)庫異常如何抽象的相關資料,需要的朋友可以參考下
    2021-09-09
  • Spring Boot 入門教程

    Spring Boot 入門教程

    相信很多人都接觸spring框架很長時間了,每次搭建spring框架的時候都需要配置好多的jar、xml,做很多繁瑣重復的配置,稍微不留神就會出現(xiàn)各種各樣的問題。今天給大家介紹一下如何利用Spring Boot快速的搭建一個簡單的web應用
    2017-03-03
  • 基于spring中的aop簡單實例講解

    基于spring中的aop簡單實例講解

    下面小編就為大家?guī)硪黄趕pring中的aop簡單實例講解。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-07-07
  • Java基礎之線程鎖相關知識總結(jié)

    Java基礎之線程鎖相關知識總結(jié)

    今天給大家?guī)淼氖顷P于Java線程的相關知識,文章圍繞著Java線程鎖展開,文中有非常詳細的介紹及代碼示例,需要的朋友可以參考下
    2021-06-06

最新評論