国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

【實(shí)戰(zhàn)Java高并發(fā)程序設(shè)計(jì)2】無鎖的對象引用:AtomicReference

lucas / 1726人閱讀

摘要:實(shí)戰(zhàn)高并發(fā)程序設(shè)計(jì)連載中的指針類和非常類似,不同之處就在于是對整數(shù)的封裝,而則對應(yīng)普通的對象引用。這樣,當(dāng)前線程就無法正確判斷這個(gè)對象究竟是否被修改過。摘自實(shí)戰(zhàn)高并發(fā)程序設(shè)計(jì)一書

【實(shí)戰(zhàn)Java高并發(fā)程序設(shè)計(jì)】連載1–Java中的指針:Unsafe類

AtomicReference和AtomicInteger非常類似,不同之處就在于AtomicInteger是對整數(shù)的封裝,而AtomicReference則對應(yīng)普通的對象引用。也就是它可以保證你在修改對象引用時(shí)的線程安全性。在介紹AtomicReference的同時(shí),我希望同時(shí)提出一個(gè)有關(guān)原子操作的邏輯上的不足。

之前我們說過,線程判斷被修改對象是否可以正確寫入的條件是對象的當(dāng)前值和期望是否一致。這個(gè)邏輯從一般意義上來說是正確的。但有可能出現(xiàn)一個(gè)小小的例外,就是當(dāng)你獲得對象當(dāng)前數(shù)據(jù)后,在準(zhǔn)備修改為新值前,對象的值被其他線程連續(xù)修改了2次,而經(jīng)過這2次修改后,對象的值又恢復(fù)為舊值。這樣,當(dāng)前線程就無法正確判斷這個(gè)對象究竟是否被修改過。如圖4.2所示,顯示了這種情況。


圖4.2 對象值被反復(fù)修改回原數(shù)據(jù)

一般來說,發(fā)生這種情況的概率很小。而且即使發(fā)生了,可能也不是什么大問題。比如,我們只是簡單得要做一個(gè)數(shù)值加法,即使在我取得期望值后,這個(gè)數(shù)字被不斷的修改,只要它最終改回了我的期望值,我的加法計(jì)算就不會出錯(cuò)。也就是說,當(dāng)你修改的對象沒有過程的狀態(tài)信息,所有的信息都只保存于對象的數(shù)值本身。

但是,在現(xiàn)實(shí)中,還可能存在另外一種場景。就是我們是否能修改對象的值,不僅取決于當(dāng)前值,還和對象的過程變化有關(guān),這時(shí),AtomicReference就無能為力了。

打一個(gè)比方,如果有一家蛋糕店,為了挽留客戶,絕對為貴賓卡里余額小于20元的客戶一次性贈送20元,刺激消費(fèi)者充值和消費(fèi)。但條件是,每一位客戶只能被贈送一次。

現(xiàn)在,我們就來模擬這個(gè)場景,為了演示AtomicReference,我在這里使用AtomicReference實(shí)現(xiàn)這個(gè)功能。首先,我們模擬用戶賬戶余額。

定義用戶賬戶余額:

static AtomicReference money=newAtomicReference();  
// 設(shè)置賬戶初始值小于20,顯然這是一個(gè)需要被充值的賬戶  
money.set(19); 

接著,我們需要若干個(gè)后臺線程,它們不斷掃描數(shù)據(jù),并為滿足條件的客戶充值。

01 //模擬多個(gè)線程同時(shí)更新后臺數(shù)據(jù)庫,為用戶充值  
02 for(int i = 0 ; i < 3 ; i++) {               
03     new Thread(){   
04         publicvoid run() {   
05            while(true){  
06                while(true){  
07                    Integer m=money.get();  
08                    if(m<20){  
09                        if(money.compareAndSet(m, m+20)){  
10                  System.out.println("余額小于20元,充值成功,余額:"+money.get()+"元");  
11                             break;  
12                        }  
13                    }else{  
14                        //System.out.println("余額大于20元,無需充值");  
15                         break ;  
16                    }  
17                 }  
18             }  
19         }   
20     }.start();  
21 }  

上述代碼第8行,判斷用戶余額并給予贈予金額。如果已經(jīng)被其他用戶處理,那么當(dāng)前線程就會失敗。因此,可以確保用戶只會被充值一次。
此時(shí),如果很不幸的,用戶正好正在進(jìn)行消費(fèi),就在贈予金額到賬的同時(shí),他進(jìn)行了一次消費(fèi),使得總金額又小于20元,并且正好累計(jì)消費(fèi)了20元。使得消費(fèi)、贈予后的金額等于消費(fèi)前、贈予前的金額。這時(shí),后臺的贈予進(jìn)程就會誤以為這個(gè)賬戶還沒有贈予,所以,存在被多次贈予的可能。下面,模擬了這個(gè)消費(fèi)線程:

01 //用戶消費(fèi)線程,模擬消費(fèi)行為  
02 new Thread() {   
03     public voidrun() {   
04         for(inti=0;i<100;i++){  
05            while(true){  
06                Integer m=money.get();  
07                 if(m>10){  
08                    System.out.println("大于10元");  
09                    if(money.compareAndSet(m, m-10)){  
10                        System.out.println("成功消費(fèi)10元,余額:"+money.get());  
11                        break;  
12                    }  
13                }else{  
14                    System.out.println("沒有足夠的金額");  
15                    break;  
16                 }  
17             }  
18             try{Thread.sleep(100);} catch (InterruptedException e) {}  
19         }  
20     }   
21 }.start();   

述代碼中,消費(fèi)者只要貴賓卡里的錢大于10元,就會立即進(jìn)行一次10元的消費(fèi)。執(zhí)行上述程序,得到的輸出如下:

余額小于20元,充值成功,余額:39元  
大于10元  
成功消費(fèi)10元,余額:29  
大于10元  
成功消費(fèi)10元,余額:19  
余額小于20元,充值成功,余額:39元  
大于10元  
成功消費(fèi)10元,余額:29  
大于10元  
成功消費(fèi)10元,余額:39  
余額小于20元,充值成功,余額:39元  

從這一段輸出中,可以看到,這個(gè)賬戶被先后反復(fù)多次充值。其原因正是因?yàn)橘~戶余額被反復(fù)修改,修改后的值等于原有的數(shù)值。使得CAS操作無法正確判斷當(dāng)前數(shù)據(jù)狀態(tài)。
雖然說這種情況出現(xiàn)的概率不大,但是依然是有可能的出現(xiàn)的。因此,當(dāng)業(yè)務(wù)上確實(shí)可能出現(xiàn)這種情況時(shí),我們也必須多加防范。體貼的JDK也已經(jīng)為我們考慮到了這種情況,使用AtomicStampedReference就可以很好的解決這個(gè)問題。

摘自《實(shí)戰(zhàn)Java高并發(fā)程序設(shè)計(jì)》一書

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/65492.html

相關(guān)文章

  • 實(shí)戰(zhàn)Java并發(fā)程序設(shè)計(jì)6】挑戰(zhàn)無鎖算法

    摘要:在本例中,講述的無鎖來自于并發(fā)包我們將這個(gè)無鎖的稱為。在這里,我們使用二維數(shù)組來表示的內(nèi)部存儲,如下變量存放所有的內(nèi)部元素。為什么使用二維數(shù)組去實(shí)現(xiàn)一個(gè)一維的呢這是為了將來進(jìn)行動(dòng)態(tài)擴(kuò)展時(shí)可以更加方便。 我們已經(jīng)比較完整得介紹了有關(guān)無鎖的概念和使用方法。相對于有鎖的方法,使用無鎖的方式編程更加考驗(yàn)一個(gè)程序員的耐心和智力。但是,無鎖帶來的好處也是顯而易見的,第一,在高并發(fā)的情況下,它比有鎖...

    zengdongbao 評論0 收藏0
  • 實(shí)戰(zhàn)Java并發(fā)程序設(shè)計(jì)5】讓普通變量也享受原子操作

    摘要:有時(shí)候,由于初期考慮不周,或者后期的需求變化,一些普通變量可能也會有線程安全的需求。它可以讓你在不改動(dòng)或者極少改動(dòng)原有代碼的基礎(chǔ)上,讓普通的變量也享受操作帶來的線程安全性,這樣你可以修改極少的代碼,來獲得線程安全的保證。 有時(shí)候,由于初期考慮不周,或者后期的需求變化,一些普通變量可能也會有線程安全的需求。如果改動(dòng)不大,我們可以簡單地修改程序中每一個(gè)使用或者讀取這個(gè)變量的地方。但顯然,這...

    appetizerio 評論0 收藏0
  • Java 中15種鎖的介紹:公平鎖,可重入鎖,獨(dú)享鎖,互斥鎖,樂觀鎖,分段鎖,自旋鎖等等

    摘要:公平鎖非公平鎖公平鎖公平鎖是指多個(gè)線程按照申請鎖的順序來獲取鎖。加鎖后,任何其他試圖再次加鎖的線程會被阻塞,直到當(dāng)前進(jìn)程解鎖。重量級鎖會讓其他申請的線程進(jìn)入阻塞,性能降低。 Java 中15種鎖的介紹 在讀很多并發(fā)文章中,會提及各種各樣鎖如公平鎖,樂觀鎖等等,這篇文章介紹各種鎖的分類。介紹的內(nèi)容如下: 公平鎖 / 非公平鎖 可重入鎖 / 不可重入鎖 獨(dú)享鎖 / 共享鎖 互斥鎖 / 讀...

    LeoHsiun 評論0 收藏0
  • Java多線程進(jìn)階(十四)—— J.U.C之a(chǎn)tomic框架:AtomicReference

    摘要:但是,有些操作會依賴于對象的變化過程,此時(shí)的解決思路一般就是使用版本號。在變量前面追加上版本號,每次變量更新的時(shí)候把版本號加一,那么就會變成。四的引入就是上面所說的加了版本號的。 showImg(https://segmentfault.com/img/remote/1460000016012188); 本文首發(fā)于一世流云的專欄:https://segmentfault.com/blo...

    aboutU 評論0 收藏0
  • [Java并發(fā)-12] 原子類:無鎖工具類的典范

    摘要:并發(fā)包將這種無鎖方案封裝提煉之后,實(shí)現(xiàn)了一系列的原子類。無鎖方案相對互斥鎖方案,最大的好處就是性能。作為一條指令,指令本身是能夠保證原子性的。 前面我們多次提到一個(gè)累加器的例子,示例代碼如下。在這個(gè)例子中,add10K() 這個(gè)方法不是線程安全的,問題就出在變量 count 的可見性和 count+=1 的原子性上。可見性問題可以用 volatile 來解決,而原子性問題我們前面一直都...

    h9911 評論0 收藏0

發(fā)表評論

0條評論

lucas

|高級講師

TA的文章

閱讀更多
最新活動(dòng)
閱讀需要支付1元查看
<