【實戰Java高併發程式設計 5】讓普通變數也享受原子操作

来源:http://www.cnblogs.com/love-jishu/archive/2016/02/18/5198748.html
-Advertisement-
Play Games

有時候,由於初期考慮不周,或者後期的需求變化,一些普通變數可能也會有線程安全的需求。


【實戰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高併發程式設計》


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 經常分不清楚目錄路徑的問題,今天自己總結一下: ~/表示文件所在的根目錄。 ../表示文件所在的上級目錄。 在Server.Mappath()方法中,測試的結果是:~/ ../ / ./ 都表示文件所在的路徑。 / 和 \ 使用的方面: /用於網站網址路徑中。而且表示./ ../ ~/ 都有 /符號
  • 最近在做的一個項目其中的一部分是與遠程伺服器進行交互,確定身份驗證的合法性,於是編寫了SendRequest方法 此方法發送給遠程伺服器XML請求,伺服器經過處理後,返回XML回應,由此方法接收到後進行返回。 1 protected string SendRequest(string strXML)
  • 一、創建拖動組件 0.Draggable組件不依賴於其他組件 1.使用標簽創建 拖動組件 2.使用js創建 拖動組件 二、屬性 1.revert:如果設置為true,在拖動停止時元素將返回起始位置 2.cursor:拖動時的CSS指針樣式 $(function () { $("#box").drag...
  • 在這篇文章中,將介紹一些提高 ASP.NET Web 應用性能的方法和技巧。眾所周知,解決性能問題是一項繁瑣的工作,當出現性能問題,每個人都會歸咎於編寫代碼的開發人員。 以下為譯文 那性能問題到底該如何解決?以下是應用系統發佈前,作為 .NET 開發人員需要檢查的點。 1.debug=「false」
  • Given n non-negative integers a1, a2, ..., an, where each represents a point at coordinate (i, ai). n vertical lines are drawn such that the two endpo
  • 在eclipse里jsp編譯後的java和class文件的位置 eclipse版本不一樣,位置也不一樣第一種:1.java類編譯後產生的.class文件在D:\workspace\test\WEB-INF\classes下; 2.jsp產生的JAVA類文件則在D:\workspace\test\wo
  • 話說用了就要有點產出,要不然過段時間又忘了,所以在這裡就記錄一下試用Kafka的安裝過程和php擴展的試用。 實話說,如果用於隊列的話,跟PHP比較配的,還是Redis。用的順手,呵呵,只是Redis不能有多個consumer。但Kafka官方對PHP不支持,PHP擴展是愛好者或使用者寫的。下麵就開
  • 一、簡介 http://www.xuebuyuan.com/2195578.html 二、教程 http://dev.ariel-networks.com/apr/
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...