【實戰Java高並發程式設計 5】讓普通變數也享受原子操作,java程式設計
【實戰Java高並發程式設計 1】Java中的指標:Unsafe類
【實戰Java高並發程式設計 2】無鎖的對象引用:AtomicReference
【實戰Java高並發程式設計 3】帶有時間戳記的對象引用:AtomicStampedReference
【實戰Java高並發程式設計 4】數組也能無鎖:AtomicIntegerArray
有時候,由於初期考慮不周,或者後期的需求變化,一些普通變數可能也會有安全執行緒的需求。如果改動不大,我們可以簡單地修改程式中每一個使用或者讀取這個變數的地方。但顯然,這樣並不符合軟體設計中的一條重要原則——開閉原則。也就是系統對功能的增加應該是開發的,而對修改應該是相對保守的。而且,如果系統裡使用到這個變數的地方特別多,一個一個修改也是一件令人厭煩的事情(況且很多使用情境下可能只是唯讀,並無線程安全的強烈要求,完全可以保持原樣)。
如果你有這種困擾,在這雷根本不需要擔心,因為在原子包裡還有一個實用的工具類AtomicIntegerFieldUpdater。它可以讓你在不改動(或者極少改動)原有代碼的基礎上,讓普通的變數也享受CAS操作帶來的執行緒安全性,這樣你可以修改極少的代碼,來獲得安全執行緒的保證。這聽起來是不是讓人很激動呢?
根據資料類型不同,這個Updater有3種,分別是AtomicIntegerFieldUpdater、AtomicLongFieldUpdater和AtomicReferenceFieldUpdater。顧名思義,它們分別可以對int、long和普通對象就行CAS修改。
現在來思考這麼一個情境。假設某地要進行一次選舉。現在類比這個機票情境,如果選民投了候選人一票,就記為1,否則記為0。最終的選票顯然就是所有資料的簡單求和。
public class AtomicIntegerFieldUpdaterDemo { public static class Candidate{ int id; volatile int score; } public final static AtomicIntegerFieldUpdater<Candidate> scoreUpdater = AtomicIntegerFieldUpdater.newUpdater(Candidate.class, "score"); //檢查Updater是否工作正確 public static AtomicInteger allScore=new AtomicInteger(0); public static void main(String[] args) throws InterruptedException { final Candidate stu=new Candidate(); Thread[] t=new Thread[10000]; for(int i = 0 ; i < 10000 ; i++) { t[i]=new Thread() { public void run() { if(Math.random()>0.4){ scoreUpdater.incrementAndGet(stu); allScore.incrementAndGet(); } } }; t[i].start(); } for(int i = 0 ; i < 10000 ; i++) { t[i].join();} System.out.println("score="+stu.score); System.out.println("allScore="+allScore); }}
上述代碼類比了這個計票情境。候選人的得票數量記錄在Candidate.score中。注意,它是一個普通的volatile變數。而volatile變數並不是安全執行緒的。第6~7行定義了AtomicIntegerFieldUpdater執行個體,用來對Candidate.score進行寫入。而後續的allScore我們用來檢查AtomicIntegerFieldUpdater的正確性。如果AtomicIntegerFieldUpdater真的保證了安全執行緒,那麼最終Candidate.score和allScore的值必然是相等的。否則,就說明AtomicIntegerFieldUpdater根本沒有確保安全執行緒的寫入。第12~21行類比了計票過程,這裡假設有大約60%的人附議,並且投票是隨機進行的。第17行使用Updater修改Candidate.score(這裡應該是安全執行緒的),第18行使用AtomicInteger計數,作為參考基準。
大家如果運行這段程式,不難發現,最終的Candidate.score總是和allScore絕對相等。這說明AtomicIntegerFieldUpdater很好地保證了Candidate.score的安全執行緒。
雖然AtomicIntegerFieldUpdater很好用,但是還是有幾個注意事項:
第一,Updater只能修改它可見範圍內的變數。因為Updater使用反射得到這個變數。如果變數不可見,就會出錯。比如如果score申明為private,就是不可行的。
第二,為了確保變數被正確的讀取,它必須是volatile類型的。如果我們原有代碼中未申明這個類型,那麼簡單得申明一下就行,這不會引起什麼問題。
第三,由於CAS操作會通過對象執行個體中的位移量直接進行賦值,因此,它不支援static欄位(Unsafe. objectFieldOffset()不支援靜態變數)。
好了,通過AtomicIntegerFieldUpdater,是不是讓我們可以更加隨心所欲得對系統關鍵資料進行安全執行緒地保護呢?
摘自《實戰Java高並發程式設計》