真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

CAS與java樂觀鎖怎么用

這篇文章主要講解了“CAS與java樂觀鎖怎么用”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“CAS與java樂觀鎖怎么用”吧!

10年積累的成都網(wǎng)站建設(shè)、網(wǎng)站建設(shè)經(jīng)驗(yàn),可以快速應(yīng)對客戶對網(wǎng)站的新想法和需求。提供各種問題對應(yīng)的解決方案。讓選擇我們的客戶得到更好、更有力的網(wǎng)絡(luò)服務(wù)。我雖然不認(rèn)識你,你也不認(rèn)識我。但先做網(wǎng)站后付款的網(wǎng)站建設(shè)流程,更有漢源免費(fèi)網(wǎng)站建設(shè)讓你可以放心的選擇與我們合作。

什么是CAS

CAS是CompareAndSwap,即比較和交換。為什么CAS沒有用到鎖還能保證并發(fā)情況下安全的操作數(shù)據(jù)呢,名字其實(shí)非常直觀的表明了CAS的原理,具體修改數(shù)據(jù)過程如下:

  1. 用CAS操作數(shù)據(jù)時,將數(shù)據(jù)原始值和要修改的值一并傳遞給方法
  2. 比較當(dāng)前目標(biāo)變量值與傳進(jìn)去的原始值是否相同
  3. 如果相同,表示目標(biāo)變量沒有被其他線程修改,直接修改目標(biāo)變量值即可
  4. 如果目標(biāo)變量值與原始值不同,那么證明目標(biāo)變量已經(jīng)被其他線程修改過,本次CAS修改失敗

從上述過程可以看到CAS其實(shí)保證的是安全的修改數(shù)據(jù),但是修改存在失敗的可能性,即目標(biāo)變量數(shù)據(jù)修改不成功,這個時候我們要循環(huán)判斷CAS修改數(shù)據(jù)結(jié)果,如果失敗進(jìn)行重試。

思維比較縝密的同學(xué)可能擔(dān)心CAS本身這個比較與替換的操作產(chǎn)生并發(fā)安全問題,實(shí)際應(yīng)用中這種情況不會發(fā)生,比較與替換由JDK借助硬件級別的CAS原語來保證比較替換是一個原子性動作。

CAS實(shí)現(xiàn)無鎖編程

無鎖編程指的是在不使用鎖的情況下保證安全的操作共享變量在并發(fā)編程中,我們用各種鎖來保證共享變量的安全性。即在保證一個線程未操作完共享變量的時候其他線程不能操作同一共享變量。
正確的使用鎖可以保證并發(fā)情況下數(shù)據(jù)安全,但是在并發(fā)程度不高,競爭不激烈的時候,獲取鎖和釋放鎖就成了沒必要的性能浪費(fèi)。這種情況下可以可考慮利用CAS保證數(shù)據(jù)安全,實(shí)現(xiàn)無鎖編程 

頭疼的ABA問題

上面我們已經(jīng)了解了CAS保證安全操作共享變量的原理,但是上述CAS操作還存在缺陷。假設(shè)當(dāng)前線程訪問的共享變量值為A,在線程1訪問共享變量過程中,線程2操作共享變量將其賦值為B,線程2處理完自己的邏輯后又將共享變量賦值為A。這時線程1比較共享變量值A(chǔ)與原始值A(chǔ)相同,誤以為沒有其他線程操作共享變量,直接返回操作成功。這就是ABA問題。雖然大部分業(yè)務(wù)不需要關(guān)心共享變量是否有過其他更改,只要原始值與當(dāng)前值一致就能得到正確的結(jié)果,但是有一些敏感場景不光要考慮共享變量結(jié)果上等同于沒有被修改過,同時也不能接受共享變量過程上被其他線程修改過。幸運(yùn)的是ABA問題也有成熟的解決方案,我們?yōu)楣蚕碜兞刻砑右粋€版本號,每當(dāng)共享變量被修改這個版本號值就會自增。在CAS操作中我們比較的不是原始變量值,而是共享變量的版本號。每次操作共享變量更新的版本號都是唯一的,所以能夠避免ABA問題。 

具體應(yīng)用場景 

JDK中的CAS應(yīng)用

首先多個線程對普通變量進(jìn)行并發(fā)操作是不安全的,一個線程的操作結(jié)果可能被其他線程覆蓋掉,比如現(xiàn)在我們用兩個線程,每個線程將初始值為1的共享變量增加一,如果沒有同步機(jī)制的話共享變量結(jié)果很可能小于3。即可能線程1和線程2都讀到了初始值1,線程1將其賦值為2,線程2所在內(nèi)存讀取到的值還是1不會變,線程2也將變量增加1然后賦值成2,這樣最終結(jié)果是2小于預(yù)期結(jié)果3。自增操作不是原子性操作導(dǎo)致了這個共享變量操作不安全問題。為了解決這個問題,JDK提供了一系列原子類提供相應(yīng)的原子操作。下面是AtomicInteger中的getAndIncrement方法源碼,讓我們從源碼來看是怎么利用CAS實(shí)現(xiàn)線程安全的原子性的整形變量相加操作。

/**
* 原子性的將當(dāng)前值增加1
*
* @return 返回自增前的值
*/
public final int getAndIncrement() {
   return unsafe.getAndAddInt(this, valueOffset, 1);
}
 

可以看到getAndIncrement實(shí)際調(diào)用了UnSafe類的getAndAddInt方法實(shí)現(xiàn)原子操作,下面是getAndAddInt源代碼

/**
* 原子的將給定值與目標(biāo)字變量相加并重新賦值給目標(biāo)變量
*
* @param o 要更新的變量所在的對象
* @param offset 變量字段的內(nèi)存偏移值
* @param delta 要增加的數(shù)字值
* @return 更改前的原始值
* @since 1.8
*/
public final int getAndAddInt(Object o, long offset, int delta) {
   int v;
   do {
    // 獲取當(dāng)前目標(biāo)目標(biāo)變量值
       v = getIntVolatile(o, offset);
   // 這句代碼是關(guān)鍵, 自旋保證相加操作一定成功
   // 如果不成功繼續(xù)運(yùn)行上一句代碼, 獲取被其他
   // 線程搶先修改的變量值, 在新值基礎(chǔ)上嘗試相加
   // 操作, 保證了相加操作的原子性
   } while (!compareAndSwapInt(o, offset, v, v + delta));
   return v;
}
 

我們都對鎖很熟悉, 比如可重入鎖ReentrantLock, JDK提供的各種鎖基本都依賴AbstractQueuedSynchronizer這個類, 當(dāng)多個線程嘗試獲取鎖時會進(jìn)入一個隊列等待, 其中多線程入隊操作的原子性就是用CAS來保證的. 源代碼如下:

/**
* 鎖底層等待獲取鎖的線程入隊操作
* @param node 要入隊的線程節(jié)點(diǎn)
* @return 入隊節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn)
*/
private Node enq(final Node node) {
// 自旋等待節(jié)點(diǎn)入隊, 通過cas保證并發(fā)情況下node安全正確入隊
   for (;;) {
       Node t = tail;
       // head為空時構(gòu)造dummy node初始化head和tail
       if (t == null) {
           if (compareAndSetHead(new Node()))
               tail = head;
       } else {
           node.prev = t;
           // 如果cas設(shè)置tail失敗了
           // 下個循環(huán)取到了最新的其他線程搶先設(shè)置的tail
           // 繼續(xù)嘗試設(shè)置.
           if (compareAndSetTail(t, node)) {
               t.next = node;
               return t;
           }
       }
   }
}
/**
* 原子性的設(shè)置tail尾節(jié)點(diǎn)為新入隊的節(jié)點(diǎn)
*/
private final boolean compareAndSetTail(Node expect, Node update) {
// 可以看到此處又是調(diào)用了Unsafe類下的原子操作方法
// 如果目標(biāo)字段(tail尾節(jié)點(diǎn)字段)當(dāng)前值是預(yù)期值
// 即沒有被其他線程搶先修改成功, 那么就設(shè)置成功
// 返回true
   return unsafe.compareAndSwapObject(this, tailOffset, expect, update);
}
   

企業(yè)開發(fā)中的樂觀鎖應(yīng)用

除了JDK中Uusafe類提供的各種原子性操作外,我們實(shí)際開發(fā)中可以用CAS思想保證并發(fā)情況下安全的操作數(shù)據(jù)庫。假設(shè)有user表結(jié)構(gòu)以及數(shù)據(jù)如下, version字段是實(shí)現(xiàn)樂觀鎖的關(guān)鍵

idusercoupon_numversion
1朱小明00

假設(shè)我們有一個用戶領(lǐng)取優(yōu)惠券的按鈕,怎么防止用戶快速點(diǎn)擊按鈕造成重復(fù)領(lǐng)取優(yōu)惠券的情況呢。我們要安全的更改id為1的用戶的coupon_num優(yōu)惠券數(shù)量,將version字段作為CAS比較的版本號,即可避免重復(fù)增加優(yōu)惠券數(shù)量,比較和替換這個邏輯通過WHERE條件來實(shí)現(xiàn). 涉及sql如下:

UPDATE user
SET coupon_num = coupon_num + 1, version = version + 1
WHERE version = 0

可以看到,我們查詢出id為1的數(shù)據(jù), 版本號為0,修改數(shù)據(jù)的同時把當(dāng)前版本號當(dāng)做條件即可實(shí)現(xiàn)安全修改,如果修改失敗,證明已經(jīng)被其他線程修改過,然后看具體業(yè)務(wù)決定是否需要自旋嘗試再次修改。這里要注意考慮競爭激烈的情況下多個線程自旋導(dǎo)致過度的性能消耗,根據(jù)并發(fā)量選擇適合自己業(yè)務(wù)的方式

感謝各位的閱讀,以上就是“CAS與java樂觀鎖怎么用”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對CAS與java樂觀鎖怎么用這一問題有了更深刻的體會,具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是創(chuàng)新互聯(lián),小編將為大家推送更多相關(guān)知識點(diǎn)的文章,歡迎關(guān)注!


本文標(biāo)題:CAS與java樂觀鎖怎么用
分享網(wǎng)址:http://weahome.cn/article/gieohj.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部