文章目錄
了解高并發(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)注支持一下杀迹。