本篇文章給大家?guī)砹岁P(guān)于java的相關(guān)問題,其中主要介紹了關(guān)于CAS的相關(guān)問題,CAS(compare and swap),比較并交換,可以解決多線程并行情況下使用鎖造成性能損耗的一種機(jī)制,希望對(duì)大家有幫助。
推薦學(xué)習(xí):《java教程》
CAS解釋:
CAS(compare and swap),比較并交換。可以解決多線程并行情況下使用鎖造成性能損耗的一種機(jī)制.CAS 操作包含三個(gè)操作數(shù)—內(nèi)存位置(V)、預(yù)期原值(A)和新值(B)。如果內(nèi)存位置的值與預(yù)期原值相匹配,那么處理器會(huì)自動(dòng)將該位置值更新為新值。否則,處理器不做任何操作。一個(gè)線程從主內(nèi)存中得到num值,并對(duì)num進(jìn)行操作,寫入值的時(shí)候,線程會(huì)把第一次取到的num值和主內(nèi)存中num值進(jìn)行比較,如果相等,就會(huì)將改變后的num寫入主內(nèi)存,如果不相等,則一直循環(huán)對(duì)比,知道成功為止。
CAS產(chǎn)生:
在修飾共享變量的時(shí)候經(jīng)常使用volatile關(guān)鍵字,但是volatile值有可見性和禁止指令重拍(有序性),無法保證原子性。雖然在單線程中沒有問題,但是多線程就會(huì)出現(xiàn)各種問題,造成現(xiàn)場(chǎng)不安全的現(xiàn)象。所以jdk1.5后產(chǎn)生了CAS利用CPU原語(yǔ)(不可分割,連續(xù)不中斷)保證現(xiàn)場(chǎng)操作原子性。
CAS應(yīng)用:
在JDK1.5 中新增java.util.concurrent(JUC)就是建立在CAS之上的。相對(duì)于對(duì)于synchronized這種鎖機(jī)制,CAS是非阻塞算法的一種常見實(shí)現(xiàn)。所以JUC在性能上有了很大的提升。
比如AtomicInteger類,AtomicInteger是線程安全的的,下面是源碼
進(jìn)入unsafe看到do while自循環(huán),這里的自循環(huán),就是在 判斷預(yù)期原值 如果與原來的值不符合,會(huì)再循環(huán)取原值,再走CAS流程,直到能夠把新值賦值成功。
CAS優(yōu)點(diǎn)
cas是一種樂觀鎖的思想,而且是一種非阻塞的輕量級(jí)的樂觀鎖,非阻塞式是指一個(gè)線程的失敗或者掛起不應(yīng)該影響其他線程的失敗或掛起的算法。
CAS 缺點(diǎn)
- 循環(huán)時(shí)間長(zhǎng)開銷大,占用CPU資源。如果自旋鎖長(zhǎng)時(shí)間不成功,會(huì)給CPU帶來很大的開銷。如果JVM能支持處理器提供的pause指令那么效率會(huì)有一定的提升,pause指令有兩個(gè)作用,第一它可以延遲流水線執(zhí)行指令(de-pipeline),使CPU不會(huì)消耗過多的執(zhí)行資源,延遲的時(shí)間取決于具體實(shí)現(xiàn)的版本,在一些處理器上延遲時(shí)間是零。第二它可以避免在退出循環(huán)的時(shí)候因內(nèi)存順序沖突(memory order violation)而引起CPU流水線被清空(CPU pipeline flush),從而提高CPU的執(zhí)行效率。
- 只能保證一個(gè)共享變量的原子操作。當(dāng)對(duì)一個(gè)共享變量執(zhí)行操作時(shí),我們可以使用循環(huán)CAS的方式來保證原子操作,但是對(duì)多個(gè)共享變量操作時(shí),循環(huán)CAS就無法保證操作的原子性,這個(gè)時(shí)候就可以用鎖,或者有一個(gè)取巧的辦法,就是把多個(gè)共享變量合并成一個(gè)共享變量來操作。比如有兩個(gè)共享變量i=2,j=a,合并一下ij=2a,然后用CAS來操作ij。從Java1.5開始JDK提供了AtomicReference類來保證引用對(duì)象之間的原子性,你可以把多個(gè)變量放在一個(gè)對(duì)象里來進(jìn)行CAS操作。
- ABA問題
解決ABA問題(如果值考慮收尾,不考慮過程可以忽略改問題)
- 添加版本號(hào)
- AtomicStampedReference
從Java1.5開始JDK的atomic包里提供了一個(gè)類AtomicStampedReference來解決ABA問題。這個(gè)類的compareAndSet方法作用是首先檢查當(dāng)前引用是否等于預(yù)期引用,并且當(dāng)前標(biāo)志是否等于預(yù)期標(biāo)志,如 全部相等,則以原子方式將該引用和該標(biāo)志的值設(shè)置為給定的更新值。
CAS使用的時(shí)機(jī)
- 線程數(shù)較少、等待時(shí)間短可以采用自旋鎖進(jìn)行CAS嘗試拿鎖,較于synchronized高效。
- 線程數(shù)較大、等待時(shí)間長(zhǎng),不建議使用自旋鎖,占用CPU較高
推薦學(xué)習(xí):《java學(xué)習(xí)教程》