「Java多線程」JUC之CAS機(jī)制與原子類型(Atomic)

文章目錄

了解高并發(fā)必須知道的概念

了解Java并發(fā)包Concurrent發(fā)展簡(jiǎn)述

1.JUC之魔法類(Unsafe)解析

2.Unsafe實(shí)現(xiàn)CAS的核心 API

四.AtomicInteger源碼淺析

2.ABA問題的解決方案之AtomicStampedReference

2.使用Unsafe實(shí)現(xiàn)一個(gè)簡(jiǎn)單原子類型

并發(fā)編程最佳學(xué)習(xí)路線

【Java多線程】了解線程的鎖池和等待池概念

【Java基礎(chǔ)】多線程從入門到掌握

【Java多線程】線程通信

了解高并發(fā)必須知道的概念

【Java多線程】高并發(fā)修煉基礎(chǔ)之高并發(fā)必須了解的概念

了解Java并發(fā)包Concurrent發(fā)展簡(jiǎn)述

【Java多線程】JUC之Java并發(fā)包Concurrent發(fā)展簡(jiǎn)述(各版本JDK中的并發(fā)技術(shù))

了解鎖的分類

【Java多線程】成神之路中必須要了解的鎖分類

一.CAS是什么

我們平時(shí)用?(synchronized放坏,Lock)?都屬于?悲觀鎖?。它總是人為?每次修改數(shù)據(jù)之前都可能被其他人(線程)修改璧针,所以在訪問資源的時(shí)候就會(huì)對(duì)資源進(jìn)行加鎖?坦袍。 當(dāng)線程獲取到鎖后十厢,其他需要獲取鎖的線程就要?阻塞?,等待持有鎖的線程釋放鎖捂齐。

CAS機(jī)制屬于?樂觀鎖?蛮放,樂觀鎖的核心思路就是 每次不加鎖而是假設(shè)修改數(shù)據(jù)之前其他線程一定不會(huì)修改,如果因?yàn)樾薷倪^產(chǎn)生沖突就失敗就重試奠宜,直到成功為止 包颁。

CAS的全稱是?Compare And Swap?瞻想,翻譯過來就是?比較并交換?。是一種?無(wú)鎖算法?娩嚼,是Java提供的?非阻塞原子性操作?蘑险。?在不使用鎖的情況下實(shí)現(xiàn)多線程下的同步。在并發(fā)包中(?

java.util.concurrent.atomic?)?原子類型?都是使用CAS來實(shí)現(xiàn)樂觀鎖的岳悟。

特點(diǎn): CAS算法是?非阻塞?的佃迄,?當(dāng)有多個(gè)線程對(duì)內(nèi)存中的數(shù)據(jù)進(jìn)行CAS操作時(shí),CPU能保證只會(huì)有一個(gè)線程能更新成功?贵少,其余線程并不會(huì)阻塞呵俏,而是繼續(xù)嘗試獲取更新,當(dāng)然也可以主動(dòng)放棄春瞬。因此不可能出現(xiàn)死鎖的情況柴信。也就是說?無(wú)鎖操作天生免疫死鎖?。

原理:在Java中可以通過?Unsafe類?實(shí)現(xiàn)CAS操作宽气,而Unsafe類最終調(diào)用的是?native方法?随常,即在?Java中通過JNI調(diào)用C或者C+系統(tǒng)函數(shù)?,來調(diào)用?CPU提供的 cmpxchgl指令 實(shí)現(xiàn)的原子性的萄涯。

CAS算法的過程是這樣:它包含?3個(gè)參數(shù) CAS(V绪氛、E、N)

基本思想?: 將內(nèi)存位置的值V與預(yù)期值E比較涝影,如果V=E枣察,那么處理器會(huì)自動(dòng)將V更新為A。否則燃逻,處理器不能做任何操作序目。V:要更新的變量E:預(yù)期值N:新值?

.

CAS操作可以分為3個(gè)步驟:

1)?將預(yù)期值E與內(nèi)存中的值V比較;

2)?如果V值不等于E值伯襟,說明其他線程做了更新猿涨,那么什么也不做,如果E與V的值相等姆怪,那么就將V的值設(shè)置為N?(保證了新值總是基于最新的信息計(jì)算的)

3)?返回操作是否成功叛赚。

什么是預(yù)期值??:也就是 你認(rèn)為現(xiàn)在變量應(yīng)該是什么樣子稽揭,如果變量不是你想象的那樣俺附,那說明已經(jīng)被別人修改過。你就重新讀取溪掀,再次嘗試修改即可事镣。

簡(jiǎn)單的來說CAS就是在一個(gè)?死循環(huán)?中判斷?預(yù)期的值E?和?內(nèi)存中的值V?是否相等,相等的話就將?V修改為N?成功后退出循環(huán)揪胃,如果不相等的話就繼續(xù)循環(huán)直到E等于V并且更新V=N成功退出循環(huán)

注意:?CAS有循環(huán)開銷大的問題?蛮浑,因?yàn)闀?huì)一直循環(huán)到?預(yù)期值E和內(nèi)存值V相等修改成功?唠叛。?同時(shí)CAS只能保證一個(gè)共享變量的原子性的問題?。不過在?JDK1.5?之后加入了?“AtomicReference類“來保證引用對(duì)象之間的原子性沮稚。

二.Unsafe類是什么

1.JUC之魔法類(Unsafe)解析

看我的這篇文章 【Java多線程】JUC之魔法類(Unsafe)解析 就行了艺沼,很適合小白入門

2.Unsafe實(shí)現(xiàn)CAS的核心 API

1.compareAndSwapObject2.compareAndSwapInt3.compareAndSwapInt

原子變量提供的原子性來自?CAS操作?,CAS來自?Unsafe?提供的api蕴掏,然后由CPU的?cmpxchg 指令?來保證障般。

(cmpxchg是匯編指令,作用:比較并交換操作數(shù))

三.Atomic工具包

Atomic工具包是JDK1.5出現(xiàn)的并發(fā)工具盛杰,主要用于 保證變量的原子性和可見性挽荡。

1.常用原子類型

普通原子類型:提供對(duì)boolean、int即供、long和對(duì)象的原子性操作定拟。

AtomicBooleanAtomicIntegerAtomicLongAtomicReference

原子類型數(shù)組:提供對(duì)數(shù)組元素的原子性操作。

AtomicLongArrayAtomicIntegerArrayAtomicReferenceArray

原子類型字段更新器:提供對(duì)指定對(duì)象的指定字段進(jìn)行原子性操作逗嫡。

AtomicLongFieldUpdaterAtomicIntegerFieldUpdaterAtomicReferenceFieldUpdater

帶版本號(hào)的原子引用類型:以版本戳的方式解決原子類型的ABA問題青自。

AtomicStampedReferenceAtomicMarkableReference

原子累加器(JDK1.8):AtomicLong和AtomicDouble的升級(jí)類型,專門用于數(shù)據(jù)統(tǒng)計(jì)驱证,性能更高延窜。

DoubleAccumulatorDoubleAdderLongAccumulatorLongAdder

2.使用案例

2.1.測(cè)試原子累加和非原子累加

publicclassAtomicIntegerTest{publicstaticAtomicInteger total =newAtomicInteger();publicstaticInteger count =0;privatestaticInteger threadNum =1000;publicstaticCountDownLatch downLatch =newCountDownLatch(threadNum);publicstaticvoidmain(String[] args) throws InterruptedException{? ? ? ? Long startTime = System.currentTimeMillis();// 創(chuàng)建1000個(gè)線程,每個(gè)線程中的run()方法將count累加1000次for(inti =0; i < threadNum; i++) {newThread(() -> {// 每個(gè)線程累加1000次for(intj =0; j < threadNum; j++) {//原子自增total.getAndIncrement();//非原子自增count++;? ? ? ? ? ? ? ? }//加入等待downLatch.countDown();? ? ? ? ? ? }).start();? ? ? ? }//等待所有線程完成downLatch.await();? ? ? ? System.out.println("非原子:"+count);? ? ? ? System.out.println("原子:"+total.get());? ? }}

執(zhí)行結(jié)果:

創(chuàng)建 1000 個(gè)線程分別對(duì)?total?進(jìn)行累加抹锄,由于沒有對(duì) total 做任何線程安全操作所以?結(jié)果一定是小于或者等于 1000000

通過 Integer 的原子類逆瑞,?AtomicInteger?類來進(jìn)行自增操作,然后調(diào)用?getAndIncrement()?伙单,保證每次都是?原子操作?获高。最終的結(jié)果輸出:?1000000?。

2.2.使用Unsafe屬性進(jìn)行原子操作

獲取屬性偏移量

通過 CAS 方式進(jìn)行修改

publicclassUnsafeTest{publicstatic Unsafe U;? ? static {try{? ? ? ? ? ? Field f = Unsafe.class.getDeclaredField("theUnsafe");? ? ? ? ? ? f.setAccessible(true);? ? ? ? ? ? U = (Unsafe)f.get(null);? ? ? ? }catch(Throwable e) {? ? ? ? ? ? e.printStackTrace();? ? ? ? }? ? }publicstatic void main(String[] args) throws NoSuchFieldException, IllegalAccessException, InstantiationException {? ? ? ? User user = new User();// 獲取字段Field age = user.getClass().getDeclaredField("age");// 獲取字段相對(duì)Java對(duì)象的"起始地址"的偏移量long offset = U.objectFieldOffset(age);// cas設(shè)置值,如果age的內(nèi)存值等于10,就將age的內(nèi)存值修改為20,返回true,否則返回falseboolean success = U.compareAndSwapInt(user, offset,10,20) ;? ? ? ? System.out.println("修改結(jié)果: "+ success ) ;// 打印數(shù)據(jù)System.out.println("查詢結(jié)果: "+ user.getAge());? ? }}classUser{privateint age;publicUser() {this.age =10;? ? }publicint getAge() {returnage; }}

執(zhí)行結(jié)果

3.>>>>>>>>>具體用法

Atomic類型的具體用法詳細(xì)說明請(qǐng)看文章 【Java基礎(chǔ)】多線程從入門到掌握 的 第十六節(jié)-使用Atomic(原子類)

四.AtomicInteger源碼淺析

publicclassAtomicIntegerextendsNumberimplementsjava.io.Serializable{privatestaticfinallongserialVersionUID =6214790243416807050L;// 獲取指針類Unsafe? ? privatestaticfinalUnsafe unsafe = Unsafe.getUnsafe();//變量value在AtomicInteger實(shí)例對(duì)象內(nèi)的內(nèi)存偏移量? ? privatestaticfinallongvalueOffset;static{try{//通過unsafe類的objectFieldOffset()方法吻育,獲取value變量在對(duì)象內(nèi)存中的偏移量? ? ? ? ? //通過該偏移量valueOffset念秧,使用unsafe類的內(nèi)部方法可以直接對(duì)內(nèi)存中value進(jìn)行取值或賦值操作? ? ? ? ? ? valueOffset = unsafe.objectFieldOffset(AtomicInteger.class.getDeclaredField("value"));? ? ? ? ? ? ? ? ? }catch(Exception ex) {thrownewError(ex); }? ? ? ? }//當(dāng)前AtomicInteger封裝的int變量value,該屬性通過volatile保證其在線程間是可見性扫沼。privatevolatileintvalue;//構(gòu)造方法? publicAtomicInteger(intinitialValue){? ? ? ? ? ? ? ? value = initialValue;? ? ? ? }publicAtomicInteger(){? ? ? ? }//獲取當(dāng)前最新值? ? publicfinalintget(){returnvalue;? ? ? ? }//設(shè)置當(dāng)前值,具備volatile效果庄吼,方法用final修飾是為了更進(jìn)一步的保證線程安全缎除。? ? publicfinalvoidset(intnewValue){? ? ? ? ? ? ? ? value = newValue;? ? ? ? }//最終會(huì)設(shè)置成newValue,使用該方法后可能導(dǎo)致其他線程在之后的一小段時(shí)間內(nèi)可以獲取到舊值总寻,有點(diǎn)類似于延遲加載publicfinalvoidlazySet(intnewValue){? ? ? ? ? ? ? ? unsafe.putOrderedInt(this, valueOffset, newValue);? ? }//設(shè)置新值并獲取舊值器罐,底層調(diào)用的是CAS操作即unsafe.compareAndSwapInt()方法publicfinalintgetAndSet(intnewValue){returnunsafe.getAndSetInt(this, valueOffset, newValue);? ? }//如果當(dāng)前值為預(yù)期值expect,則設(shè)置為update(當(dāng)前值指的是value變量)? ? publicfinalbooleancompareAndSet(intexpect,intupdate){returnunsafe.compareAndSwapInt(this, valueOffset, expect, update);? ? ? ? }//當(dāng)前值加1返回舊值渐行,底層CAS操作? ? publicfinalintgetAndIncrement(){returnunsafe.getAndAddInt(this, valueOffset,1);? ? ? }//當(dāng)前值減1轰坊,返回舊值铸董,底層CAS操作? ? publicfinalintgetAndDecrement(){returnunsafe.getAndAddInt(this, valueOffset,-1);? ? ? ? }//當(dāng)前值增加delta,返回舊值肴沫,底層CAS操作? ? publicfinalintgetAndAdd(intdelta){returnunsafe.getAndAddInt(this, valueOffset, delta);? ? ? ? }//當(dāng)前值加1粟害,返回新值,底層CAS操作publicfinalintincrementAndGet(){returnunsafe.getAndAddInt(this, valueOffset,1) +1;? ? ? ? }//當(dāng)前值減1颤芬,返回新值悲幅,底層CAS操作? ? publicfinalintdecrementAndGet(){returnunsafe.getAndAddInt(this, valueOffset,-1) -1;? ? ? ? }//當(dāng)前值增加delta,返回新值站蝠,底層CAS操作? ? publicfinalintaddAndGet(intdelta){returnunsafe.getAndAddInt(this, valueOffset, delta) + delta;? ? ? ? }//省略一些不常用的方法....}

AtomicInteger是基于Unsafe類中CAS相關(guān)操作實(shí)現(xiàn)的汰具,是無(wú)鎖操作。

publicfinalintgetAndIncrement(){returnunsafe.getAndAddInt(this, valueOffset,1);? }

調(diào)用了Unsafe類中的?getAndAddInt()?方法菱魔,該方法執(zhí)行一個(gè)?CAS操作?留荔,保證變量的?原子性。

可看出?getAndAddInt?通過一個(gè)?do-while循環(huán)不斷的重試更新要設(shè)置的值澜倦,直到成功為止聚蝶,?調(diào)用的是Unsafe類中的?compareAndSwapInt?方法,?是一個(gè)CAS操作方法肥隆。即: 如果?var1+var2?在內(nèi)存的值等于?var5?既荚,修改?var1+var2?在內(nèi)存的值為?var5 + var4(也就是?內(nèi)存值+=1?),返回true栋艳,如果不相等則返回false恰聘,繼續(xù)?循環(huán)嘗試更新

如果將?compareAndSwapInt(var1, var2, var5, var5 + var4)?換成?compareAndSwapInt(obj, offset, expect, update)?就比較清楚了,意思就是如果?obj?內(nèi)的?value?和?expect?相等吸占,就證明沒有其他線程改變過這個(gè)變量晴叨,那么就更新它為?update?,如果這一步的?CAS?沒有成功矾屯,那就采用?自旋?的方式繼續(xù)進(jìn)行?CAS?操作兼蕊,取出乍一看這也是2個(gè)步驟了啊, 其實(shí)是在JNI?里調(diào)用?C的系統(tǒng)函數(shù)?發(fā)送?CPU指令cmpxchgl?完成的件蚕。所以還是?原子操作?孙技。

publicfinalintgetAndAddInt(Object var1,longvar2,intvar4){intvar5;do{? ? ? ? var5 =this.getIntVolatile(var1, var2);? ? }while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));returnvar5;}

上述源碼分析是基于JDK1.8的,如果是1.8之前的方法實(shí)現(xiàn)如下`:

//JDK 1.7的源碼排作,由for的死循環(huán)實(shí)現(xiàn)牵啦,并且直接在AtomicInteger實(shí)現(xiàn)該方法,//JDK1.8后妄痪,該方法實(shí)現(xiàn)已移動(dòng)到Unsafe類中哈雏,直接調(diào)用getAndAddInt方法即可publicfinalintincrementAndGet(){for(;;) {intcurrent = get();intnext = current +1;if(compareAndSet(current, next))returnnext;? ? ? ? }}

五.CAS缺陷

1.CAS缺點(diǎn)有哪些?

循環(huán)時(shí)間長(zhǎng)時(shí)開銷大:如果CAS不成功,則會(huì)原地自旋裳瘪,因?yàn)闀?huì)一直循環(huán)到預(yù)期值E和內(nèi)存值V相等修改成功?土浸,如果長(zhǎng)時(shí)間自旋會(huì)給CPU帶來非常大的執(zhí)行開銷。果長(zhǎng)時(shí)間自旋會(huì)給CPU帶來非常大的執(zhí)行開銷彭羹。

只能保證一個(gè)共享變量的原子操作:當(dāng)對(duì)1個(gè)共享變量執(zhí)行操作時(shí)黄伊,我們可以使用循環(huán)CAS的方式來保證原子操作,但是 對(duì)多個(gè)共享變量操作時(shí)皆怕,循環(huán)CAS就無(wú)法保證操作的原子性毅舆,這個(gè)時(shí)候就可以用?鎖?,或者有一個(gè)取巧的辦法愈腾,就是?把多個(gè)共享變量合并成一個(gè)共享變量來操作?憋活。比如有2個(gè)共享變量i=2,j=a,合并一下ij=2a虱黄,然后用CAS來操作ij悦即。在?JDK1.5?之后加入了?“AtomicReference類“來保證引用對(duì)象之間的原子性〕髀遥可以把多個(gè)變量放在一個(gè)對(duì)象里來進(jìn)行CAS操作辜梳。

ABA問題如果?內(nèi)存地址V?初次讀取的值是?A?,并且在準(zhǔn)備賦值的時(shí)候檢查到它的值仍然為?A?泳叠,那我們就能說它的值沒有被其他線程改變過了嗎作瞄?如果在這段期間它的值 曾經(jīng)被改成了B,后來又被改回為A危纫,那CAS操作就會(huì)誤認(rèn)為它從來沒有被改變過 宗挥。?無(wú)法正確判斷這個(gè)變量是否已被修改過,一般稱這種情況為ABA問題种蝶。即線程1在把A的值改成了B,線程2又把B改回了A,此時(shí)再來一個(gè)線程3操作時(shí)發(fā)現(xiàn)A的值并沒有改變,但實(shí)際上A的值是有被操作過的,為了避免在這種情況下CAS鎖失效..?ABA問題的解決思路就是使用?版本號(hào)?契耿。在變量前面追加上版本號(hào),每次變量更新的時(shí)候把版本號(hào)加1螃征,那么?A-B-A?就會(huì)變成?1A-2B-3A?搪桂。在?JDK1.5?之后加入了?“AtomicReference類“來解決ABA問題?。這個(gè)類的?compareAndSet?方法作用是 首先檢查?當(dāng)前引用?是否等于?預(yù)期引用?盯滚,并且?當(dāng)前標(biāo)志?是否等于?預(yù)期標(biāo)志?踢械,如果?全部相等?,則以原子方式將該引用和該標(biāo)志的值設(shè)置為給定的更新值魄藕。 解決了多線程反復(fù)讀寫時(shí)内列,無(wú)法預(yù)知值是否已被修改的問題。

因此泼疑,在使用CAS前要考慮清楚 “ABA”問題是否會(huì)影響程序并發(fā)的正確性德绿, 如果需要解決ABA問題,改用傳統(tǒng)的互斥同步可能會(huì)比原子類更高效退渗。

2.ABA問題的解決方案之AtomicStampedReference

在JDK1.5后. Atomic包加入了 AtomicStampedReference類通過給每個(gè)變量加入了一個(gè) 版本號(hào)來避免ABA問題移稳。每次修改變量值的操作,都會(huì)比對(duì)當(dāng)前值和期望值,當(dāng)前版本號(hào)和預(yù)期版本號(hào)会油,只有二者都相同是才能更新成功

底層實(shí)現(xiàn):由一個(gè)?自定義鍵值對(duì)Pair?存儲(chǔ)對(duì)象引用reference和版本號(hào)stamp个粱,并構(gòu)造volatile修飾的私有實(shí)例; 更新數(shù)據(jù)時(shí)翻翩,不但會(huì)對(duì)比當(dāng)前值和期望值都许,還會(huì)對(duì)比當(dāng)前版本號(hào)和預(yù)期版本號(hào),只有二者都相同嫂冻,才會(huì)調(diào)用?Unsafe的compareAndSwapObject方法執(zhí)行數(shù)值和版本號(hào)替換?胶征。

它的構(gòu)造方法有 2 個(gè)參數(shù)(?初始值,初始版本號(hào)?),在修改數(shù)據(jù)時(shí)需提供一個(gè)新的值和一個(gè)新的版本號(hào)桨仿,這樣在多線程情況下只要數(shù)據(jù)被修改了那么版本號(hào)一定會(huì)發(fā)生改變睛低,另一個(gè)線程拿到的是舊的版本號(hào)所以會(huì)修改失敗。tips:?AtomicMarkableReference?:?帶boolean值標(biāo)識(shí)的原子引用類型?服傍,true和false兩種切換狀態(tài)表示是否被修改钱雷。不靠譜。

在此之前我們先模擬一個(gè) ABA 問題:

publicclassABATest{//初始原子類型的值為10privatestaticAtomicInteger index =newAtomicInteger(10);publicstaticvoidmain(String[] args){newThread(() -> {//如果內(nèi)存值為10,則修改為101index.compareAndSet(10,101);//如果內(nèi)存值為101,則修改為10index.compareAndSet(101,10);? ? ? ? ? ? System.out.println(Thread.currentThread().getName() +": 10->101->10");? ? ? ? },"zhangSan").start();newThread(() -> {try{? ? ? ? ? ? ? ? TimeUnit.SECONDS.sleep(2);? ? ? ? ? ? }catch(InterruptedException e) {? ? ? ? ? ? ? ? e.printStackTrace();? ? ? ? ? ? }//如果內(nèi)存值為10,則修改為1000,并返回修改結(jié)果boolean result = index.compareAndSet(10,1000);? ? ? ? ? ? System.out.println(Thread.currentThread().getName() +": 更新結(jié)果:"+ result +", 更新后的值: "+ index.get());? ? ? ? },"liSi").start();? ? }}

執(zhí)行結(jié)果

通過?AtomicStampedReference?來解決這個(gè)問題 : 就是我們?cè)谶M(jìn)入第2個(gè)線程之前吹零,先讀取當(dāng)前的?版本號(hào)?罩抗,然后進(jìn)入更新。這個(gè)讀取的?版本號(hào)?可能是一個(gè)舊的值灿椅。如果出現(xiàn)這種情況套蒂,那么我們執(zhí)行?compareAndSet?就會(huì)返回失敗。

publicclassABATest2{//初始原子類型的值為10,版本號(hào)為1privatestaticAtomicStampedReference index =newAtomicStampedReference(10,1);publicstaticvoidmain(String[] args){newThread(() -> {//如果內(nèi)存值為10,且內(nèi)存版本號(hào)相等,則修改為101,且版本號(hào)+1index.compareAndSet(10,101, index.getStamp(), index.getStamp() +1);//如果內(nèi)存值為101,,且內(nèi)存版本號(hào)相等,則修改為10,且版本號(hào)+1index.compareAndSet(101,10, index.getStamp(), index.getStamp() +1);? ? ? ? ? ? System.out.println(Thread.currentThread().getName() +": 10->101->10");? ? ? ? },"zhangSan").start();//獲取當(dāng)前版本號(hào)intstamp = index.getStamp();newThread(() -> { 阱扬、try{? ? ? ? ? ? ? ? TimeUnit.SECONDS.sleep(2);? ? ? ? ? ? }catch(InterruptedException e) {? ? ? ? ? ? ? ? e.printStackTrace();? ? ? ? ? ? }//如果內(nèi)存值為10,且內(nèi)存版本號(hào)相等,則修改為1000,且版本號(hào)+1,返回true,否則什么也不做,返回falseboolean result = index.compareAndSet(10,1000, stamp, stamp +1);? ? ? ? ? ? System.out.println(Thread.currentThread().getName() +": 更新結(jié)果:"+ result +", 更新后的值: "+ index.getReference());? ? ? ? },"liSi").start();? ? }}

執(zhí)行結(jié)果

六.拓展

1.CAS實(shí)現(xiàn)單例模式

publicclassSingleton{privatestaticAtomicReference singletonAtomicReference =newAtomicReference<>();privateSingleton(){? ? }publicstaticSingletongetInstance(){while(true) {// 獲得singletonSingleton singleton = singletonAtomicReference.get();// 如果singleton不為空泣懊,就返回singletonif(singleton !=null) {returnsingleton;? ? ? ? ? ? }// 如果singleton為空,創(chuàng)建一個(gè)singletonsingleton =newSingleton();// CAS操作麻惶,預(yù)期值是NULL馍刮,新值是singleton// 如果成功,返回singleton// 如果失敗窃蹋,進(jìn)入第二次循環(huán)卡啰,singletonAtomicReference.get()就不會(huì)為空了(當(dāng)前值等于null,更新當(dāng)前值為singleton)if(singletonAtomicReference.compareAndSet(null, singleton)) {returnsingleton;? ? ? ? ? ? }? ? ? ? }? ? }publicstaticvoidmain(String[] args) throws InterruptedException{intthreadNum =1000;? ? ? ? CountDownLatch downLatch =newCountDownLatch(threadNum);for(inti =0; i < threadNum; i++) {newThread(() -> {for(intj =0; j < threadNum; j++) {? ? ? ? ? ? ? ? ? ? System.out.println(Singleton.getInstance());? ? ? ? ? ? ? ? }//加入等待downLatch.countDown();? ? ? ? ? ? }).start();? ? ? ? }//等待所有線程完成downLatch.await();? ? ? ? System.out.println("執(zhí)行結(jié)束");? ? }}

2.使用Unsafe實(shí)現(xiàn)一個(gè)簡(jiǎn)單原子類型

測(cè)試原子和非原子操作

publicclassMyAtomicInteger{privatestatic long offset;//偏移地址privatestatic Unsafe unsafe;? ? static {try{? ? ? ? ? ? Field theUnsafeField = Unsafe.class.getDeclaredField("theUnsafe");? ? ? ? ? ? theUnsafeField.setAccessible(true);? ? ? ? ? ? unsafe = (Unsafe) theUnsafeField.get(null);? ? ? ? ? ? Field field = MyAtomicInteger.class.getDeclaredField("value");? ? ? ? ? ? offset = unsafe.objectFieldOffset(field);//獲得偏移地址}catch(Exception e) {? ? ? ? ? ? e.printStackTrace();? ? ? ? }? ? }privatevolatileint value;publicvoid increment() {? ? ? ? int tempValue;do{? ? ? ? ? ? tempValue = unsafe.getIntVolatile(this, offset);//拿到值}while(!unsafe.compareAndSwapInt(this, offset, tempValue, value +1));//CAS自旋}publicintget() {returnvalue;? ? }publicfinalint incrementAndGet() {returnunsafe.getAndAddInt(this, offset,1) +1;? ? }/**? ? *@paramobj? ? 實(shí)例? ? *@paramoffset 實(shí)例變量的偏移量? ? *@paramexpect 預(yù)期值? ? *@return*/publicfinalint getAndAddInt(Object obj, long offset, intexpect) {? ? ? ? int tempValue;do{? ? ? ? ? ? tempValue = unsafe.getIntVolatile(obj, offset);//實(shí)例,示例屬性的偏移量警没,預(yù)期值匈辱,更新的值//如果偏移量的值等于預(yù)期值值,更新offset=tempValue + expect,返回true,否則不更新offset,返回false}while(!unsafe.compareAndSwapInt(obj, offset, tempValue, tempValue +expect));//CAS自旋returntempValue;? ? }}

publicclassMyAtomicIntegerTest{publicstaticMyAtomicInteger total =newMyAtomicInteger();publicstaticInteger count =0;privatestaticInteger threadNum =1000;publicstaticCountDownLatch downLatch =newCountDownLatch(threadNum);publicstaticvoidmain(String[] args) throws InterruptedException{? ? ? ? Long startTime = System.currentTimeMillis();for(inti =0; i < threadNum; i++) {newThread(() -> {for(intj =0; j < threadNum; j++) {? ? ? ? ? ? ? ? ? ? total.incrementAndGet();? ? ? ? ? ? ? ? ? ? count++;? ? ? ? ? ? ? ? }//加入等待downLatch.countDown();? ? ? ? ? ? }).start();? ? ? ? }//等待所有線程完成downLatch.await();? ? ? ? System.out.println("非原子:"+count);? ? ? ? System.out.println("原子:"+total.get());? ? }}

執(zhí)行結(jié)果

看完本文如果對(duì)你有幫助的話,可以轉(zhuǎn)發(fā)關(guān)注支持一下杀迹。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末亡脸,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌浅碾,老刑警劉巖大州,帶你破解...
    沈念sama閱讀 218,451評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異垂谢,居然都是意外死亡厦画,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,172評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門滥朱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來根暑,“玉大人,你說我怎么就攤上這事徙邻∨畔樱” “怎么了?”我有些...
    開封第一講書人閱讀 164,782評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵缰犁,是天一觀的道長(zhǎng)躏率。 經(jīng)常有香客問我,道長(zhǎng)民鼓,這世上最難降的妖魔是什么薇芝? 我笑而不...
    開封第一講書人閱讀 58,709評(píng)論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮丰嘉,結(jié)果婚禮上夯到,老公的妹妹穿的比我還像新娘。我一直安慰自己饮亏,他們只是感情好耍贾,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,733評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著路幸,像睡著了一般荐开。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上简肴,一...
    開封第一講書人閱讀 51,578評(píng)論 1 305
  • 那天晃听,我揣著相機(jī)與錄音,去河邊找鬼砰识。 笑死能扒,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的辫狼。 我是一名探鬼主播初斑,決...
    沈念sama閱讀 40,320評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼膨处!你這毒婦竟也來了见秤?” 一聲冷哼從身側(cè)響起砂竖,我...
    開封第一講書人閱讀 39,241評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎鹃答,沒想到半個(gè)月后晦溪,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,686評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡挣跋,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,878評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了狞换。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片避咆。...
    茶點(diǎn)故事閱讀 39,992評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖修噪,靈堂內(nèi)的尸體忽然破棺而出查库,到底是詐尸還是另有隱情,我是刑警寧澤黄琼,帶...
    沈念sama閱讀 35,715評(píng)論 5 346
  • 正文 年R本政府宣布樊销,位于F島的核電站,受9級(jí)特大地震影響脏款,放射性物質(zhì)發(fā)生泄漏围苫。R本人自食惡果不足惜宿刮,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,336評(píng)論 3 330
  • 文/蒙蒙 一但骨、第九天 我趴在偏房一處隱蔽的房頂上張望迎膜。 院中可真熱鬧僻焚,春花似錦之景、人聲如沸逆屡。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,912評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)痒谴。三九已至衰伯,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間积蔚,已是汗流浹背意鲸。 一陣腳步聲響...
    開封第一講書人閱讀 33,040評(píng)論 1 270
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留尽爆,地道東北人临扮。 一個(gè)月前我還...
    沈念sama閱讀 48,173評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像教翩,于是被迫代替她去往敵國(guó)和親杆勇。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,947評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容