什麼是CAS
CAS是CompareAndSwap,即比較和交換。為什麼CAS沒有用到鎖還能保證並發情況下安全的操作數據呢,名字其實非常直觀的表明了CAS的原理,具體修改數據過程如下:
用CAS操作資料時,將資料原始值和要修改的值一併傳遞給方法
比較目前目標變數值與傳進去的原始值是否相同
-
#如果相同,表示目標變數沒有被其他執行緒修改,直接修改目標變數值即可
#如果目標變數值與原始值不同,那麼證明目標變數已經被其他線程修改過,本次CAS修改失敗
從上述過程可以看到CAS其實保證的是安全的修改數據,但是修改存在失敗的可能性,即目標變量數據修改不成功,這個時候我們要循環判斷CAS修改資料結果,如果失敗重試。
思維比較縝密的同學可能擔心CAS本身這個比較與替換的操作產生並發安全問題,實際應用中這種情況不會發生,比較與替換由JDK借助硬件級別的CAS原語來保證比較替換是一個原子性動作。
CAS實作無鎖定程式設計
無鎖定程式指的是不使用鎖定的情況下保證安全的操作共享變數在並發程式設計中,我們用各種鎖來保證共享變數的安全性。即在保證一個執行緒未操作完共享變數的時候其他執行緒不能操作同一共享變數。
正確的使用鎖可以保證並發情況下資料安全,但是在並發程度不高,競爭不激烈的時候,取得鎖和釋放鎖就成了沒必要的效能浪費。這種情況下可以考慮利用CAS確保資料安全,實現無鎖定編程
頭痛的ABA問題
上面我們已經了解了CAS保證安全操作共享變數的原理,但是上述CAS操作還存在缺陷。假設目前執行緒存取的共享變數值為A,在執行緒1存取共享變數過程中,執行緒2操作共享變數將其賦值為B,執行緒2處理完自己的邏輯後又將共享變數賦值為A。這時線程1比較共享變量值A與原始值A相同,誤以為沒有其他線程操作共享變量,直接返回操作成功。這就是ABA問題。雖然大部分業務不需要關心共享變數是否有過其他更改,只要原始值與當前值一致就能得到正確的結果,但是有一些敏感場景不光要考慮共享變數結果上等同於沒有被修改過,同時也不能接受共享變數過程上被其他執行緒修改過。幸運的是ABA問題也有成熟的解決方案,我們為共享變數加上一個版本號,每當共享變數被修改這個版本號值就會自增。在CAS運算中我們比較的不是原始變數值,而是共享變數的版本號碼。每次運算共享變數更新的版本號碼都是唯一的,所以能夠避免ABA問題。
具體應用場景
JDK中的CAS應用
首先多個執行緒對普通變數進行並發操作是不安全的,一個執行緒的操作結果可能被其他執行緒覆蓋掉,例如現在我們用兩個線程,每個線程將初始值為1的共享變數增加一,如果沒有同步機制的話共享變數結果很可能小於3。即可能線程1和線程2都讀到了初始值1,線程1將其賦值為2,線程2所在內存讀取到的值還是1不會變,線程2也將變量增加1然後賦值成2,這樣最終結果是2小於預期結果3。自增操作不是原子性操作導致了這個共享變數操作不安全問題。為了解決這個問題,JDK提供了一系列原子類提供相應的原子操作。下面是AtomicInteger中的getAndIncrement方法原始碼,讓我們從原始碼來看是怎麼利用CAS實作執行緒安全的原子性的整形變數相加操作。
<code>/**<br> * 原子性的将当前值增加1<br> *<br> * @return 返回自增前的值<br> */<br>public final int getAndIncrement() {<br> return unsafe.getAndAddInt(this, valueOffset, 1);<br>}<br></code>
可以看到getAndIncrement實際呼叫了UnSafe類別的getAndAddInt方法實作原子操作,以下是getAndAddInt原始碼
<code>/**<br> * 原子的将给定值与目标字变量相加并重新赋值给目标变量<br> *<br> * @param o 要更新的变量所在的对象<br> * @param offset 变量字段的内存偏移值<br> * @param delta 要增加的数字值<br> * @return 更改前的原始值<br> * @since 1.8<br> */<br>public final int getAndAddInt(Object o, long offset, int delta) {<br> int v;<br> do {<br> // 获取当前目标目标变量值<br> v = getIntVolatile(o, offset);<br> // 这句代码是关键, 自旋保证相加操作一定成功<br> // 如果不成功继续运行上一句代码, 获取被其他<br> // 线程抢先修改的变量值, 在新值基础上尝试相加<br> // 操作, 保证了相加操作的原子性<br> } while (!compareAndSwapInt(o, offset, v, v + delta));<br> return v;<br>}<br></code>
我們都對鎖很熟悉, 例如可重入鎖ReentrantLock, JDK提供的各種鎖基本上都依賴AbstractQueuedSynchronizer這個類別, 當多個線程嘗試獲取鎖時會進入一個隊列等待, 其中多線程入隊操作的原子性就是用CAS來保證的. 原始碼如下:
<code>/**<br> * 锁底层等待获取锁的线程入队操作<br> * @param node 要入队的线程节点<br> * @return 入队节点的前驱节点<br> */<br>private Node enq(final Node node) {<br>// 自旋等待节点入队, 通过cas保证并发情况下node安全正确入队<br> for (;;) {<br> Node t = tail;<br> // head为空时构造dummy node初始化head和tail<br> if (t == null) {<br> if (compareAndSetHead(new Node()))<br> tail = head;<br> } else {<br> node.prev = t;<br> // 如果cas设置tail失败了<br> // 下个循环取到了最新的其他线程抢先设置的tail<br> // 继续尝试设置.<br> if (compareAndSetTail(t, node)) {<br> t.next = node;<br> return t;<br> }<br> }<br> }<br>}<br>/**<br> * 原子性的设置tail尾节点为新入队的节点<br> */<br>private final boolean compareAndSetTail(Node expect, Node update) {<br>// 可以看到此处又是调用了Unsafe类下的原子操作方法<br>// 如果目标字段(tail尾节点字段)当前值是预期值<br>// 即没有被其他线程抢先修改成功, 那么就设置成功<br>// 返回true<br> return unsafe.compareAndSwapObject(this, tailOffset, expect, update);<br>}</code>
企業開發中的樂觀鎖應用
除了JDK中Uusafe類別提供的各種原子性操作外,我們實際開發中可以用CAS思想保證並發情況下安全的操作資料庫。 假設有user表結構以及資料如下,version欄位是實作樂觀鎖的關鍵
#id | ##usercoupon_num | version | |
---|---|---|---|
朱小明 | #0 | 0 |
以上是CAS與java樂觀鎖怎麼用的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本文討論了使用Maven和Gradle進行Java項目管理,構建自動化和依賴性解決方案,以比較其方法和優化策略。

本文使用Maven和Gradle之類的工具討論了具有適當的版本控制和依賴關係管理的自定義Java庫(JAR文件)的創建和使用。

本文討論了使用咖啡因和Guava緩存在Java中實施多層緩存以提高應用程序性能。它涵蓋設置,集成和績效優勢,以及配置和驅逐政策管理最佳PRA

本文討論了使用JPA進行對象相關映射,並具有高級功能,例如緩存和懶惰加載。它涵蓋了設置,實體映射和優化性能的最佳實踐,同時突出潛在的陷阱。[159個字符]

Java的類上載涉及使用帶有引導,擴展程序和應用程序類負載器的分層系統加載,鏈接和初始化類。父代授權模型確保首先加載核心類別,從而影響自定義類LOA


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

VSCode Windows 64位元 下載
微軟推出的免費、功能強大的一款IDE編輯器

Dreamweaver CS6
視覺化網頁開發工具

WebStorm Mac版
好用的JavaScript開發工具

Safe Exam Browser
Safe Exam Browser是一個安全的瀏覽器環境,安全地進行線上考試。該軟體將任何電腦變成一個安全的工作站。它控制對任何實用工具的訪問,並防止學生使用未經授權的資源。

禪工作室 13.0.1
強大的PHP整合開發環境