JAVA並發編程總結:執行緒安全性、對象的共用

來源:互聯網
上載者:User

第一章 簡介

摘書

  1. 線程會共用進程範圍內的資源,例如記憶體控制代碼和檔案控制代碼,但每個線程都有各自的程式計數器(Program Counter)、棧以及局部變數等。

  2. 在同一個程式中的多個線程也可以被同時調度到多個CPU上運行。

第二章 執行緒安全性

摘書

  1. Java中的主要同步機制是關鍵字synchronized,它提供了一種獨佔的加鎖方式,但“同步”這個術語還包括volatile類型的變數,顯式鎖(Explicit Lock)以及原子變數。

  2. 如果當多個線程訪問同一個可變的狀態變數時沒有使用合適的同步,那麼程式就會出現錯誤。有三種方式可以修複這個問題:

    • 不線上程之間共用改狀態變數。

    • 將狀態變數修改為不可變的變數。

    • 在訪問狀態變數時使用同步。

  3. 執行緒安全性定義:當多個線程訪問某個類時,這個類始終都能表現出正確的行為,那麼就稱這個類是安全執行緒的。

  4. 無狀態對象一定是安全執行緒的。

  5. 大多數競態條件的本質:基於一種可能失效的觀察結果來做出判斷或者是執行某個計算。這種類型的競態條件成為“先檢查後執行”:首先觀察到某個條件為真(例如檔案X不存在),然後根據這個觀察結果採用相應的動作(建立檔案X),但事實上,在你觀察到這個結果以及開始建立檔案之間,觀察結果可能變得無效(另一個線程在這期間建立了檔案X),從而導致了各種問題(未預期的異常、資料被覆蓋、檔案被破壞等)。

  6. 假定有兩個操作A和B,如果從執行A的線程來看,當另一個線程執行B時,要麼將B全部執行完,要麼完全不執行B,那麼A和B對彼此來說是原子的。原子操作是指,對於訪問同一個狀態的所有操作(包括該操作本身)來說,這個操作是一個以原子方式執行的操作。

  7. 在實際情況中,應儘可能地使用現有的安全執行緒對象(例如AtomicLong)來管理類的狀態。與非安全執行緒的對象相比,判斷安全執行緒對象的可能狀態及其狀態轉換情況要更為容易,從而也更加容易維護和驗證執行緒安全性。

  8. 要保持狀態的一致性,就需要在單個原子操作中更新所有相關的狀態變數。

  9. 重入的一種實現方法是,為每個鎖關聯一個擷取計數值和一個所有者線程。當計數值為0時,這個鎖就被認為是沒有被任何線程持有。當線程請求一個未被持有的鎖時,JVM將會記下鎖的持有人,並且將擷取計數值置為1。如果同一個線程再次擷取這個鎖,計數值將會遞增,而當線程退出同步代碼塊時,計數器會相應地遞減。當計數值為0時,這個鎖將被釋放。

  10. 並非所有資料都需要鎖的保護,只有被多個線程同時訪問的可變資料才需要通過鎖來保護。

  11. 當執行時間較長的計算或者可能無法快速完成的操作時(例如,網路IO或控制台IO),一定不要持有鎖。

體會

  1. 狀態的理解,我認為是類的成員變數。無狀態對象就是成員變數不能儲存資料,或者是可以儲存資料但是這個資料不可變。無狀態對象是安全執行緒的。如果方法中存在成員變數,就需要對這個成員變數進行相關的安全執行緒的操作。

  2. 不要一味地在方法前加synchronized,這可以保證安全執行緒,但是方法的並發功能會減弱,導致本來可以支援並發的方法變成堵塞,導致程式處理速度的變慢。

  3. synchronized包圍的代碼要儘可能的短,但是要保證有影響的所有成員變數在一起。沒有關係的成員變數可以用多個synchronized包圍。

第三章 對象的共用

摘書

  1. 加鎖的含義不僅僅局限於互斥行為,還包括記憶體可見度。為了確保所有線程都能看到共用變數的最新值,所有執行讀操作或者寫操作的線程都必須在同一個鎖上同步。

  2. Java語言提供了一種稍弱的同步機制,即volatile變數,用來確保將變數的更新操作通知到其他線程。當把變數聲明為volatile類型後,編譯器與運行時都會注意到這個變數是共用的,因此不會將該變數上的操作與其他記憶體操作一起重新排序。volatile變數不會被緩衝在寄存器或者對其他處理器不可見的地方,因此在讀取volatile類型的變數時總會返回最新寫入的值。

  3. 在訪問volatile變數時不會執行加鎖操作,因此也就不會使執行線程阻塞,因此volatile變數是一種比synchronized關鍵字更輕量級的同步機制。

  4. volatile變數通常用做某個操作完成、發生中斷或者是狀態的標誌。volatile的語義不足以確保遞增操作(count++)的原子性,除非你能確保只有一個線程對變數執行寫操作。

  5. 加鎖機制既可以確保可見度又可以確保原子性,而volatile變數只能確保可見度。

  6. 若且唯若滿足以下所有條件時,才應該使用volatile變數:

    • 對變數的寫入操作不依賴變數的當前值,或者你能確保只有單個線程更新變數的值。

    • 該變數不會與其他狀態變數一起納入不變形條件中。

    • 訪問該變數時不需要加鎖。

  7. “發布(Publish)”一個對象的意思是指,使對象能夠在當前範圍之外的代碼中使用。

  8. 當某個不應該發布的對象被發布時,這種情況就被稱為逸出(Escape)。

  9. 不要在構造過程中使this引出逸出。

  10. 如果想在建構函式中註冊一個事件監聽器或者啟動線程,那麼可以使用一個私人的建構函式和一個公用的Factory 方法(Factory Method),從而避免不正確的構造過程。

  11. 棧封閉是線程封閉的一種特例,在棧封閉中,只有通過局部變數才能訪問對象。

  12. 維持線程封閉性的一種更規範方法是使用ThreadLocal,這個類能夠使線程中的某個值與儲存值的對象關聯起來。

  13. ThreadLocal對象通常用於防止對可變的單一實例對象(Singleton)或全域變數進行共用。

  14. 當滿足以下條件時,對象才是不可變的:

    • 對象建立以後其狀態就不能修改。

    • 對象的所有域都是final類型。

    • 對象是正確建立的(在對象的建立期間,this引用沒有逸出)。

  15. 不可變對象一定是安全執行緒的。

  16. 要安全地發布一個對象,對象的引用以及對象的狀態必須同時對其他線程可見。一個正確構造的對象可以通過以下方式來安全地發布:

    • 在靜態初始化函數中初始化一個對象引用。

    • 將對象的引用儲存到volatile類型的域或者AtomicReferance對象中。

    • 將對象的引用儲存到某個正確構造對象的final類型域中。

    • 將對象的引用儲存到一個由鎖保護的域中。

  17. 在沒有額外的同步的情況下,任何線程都可以安全地使用被安全發布的事實不可變對象。

  18. 對象的發布需求取決於它的可變性:

    • 不可變對象可以通過任意機制來發布。

    • 事實不可變對象必須通過安全方式來發布。

    • 可變對象必須通過安全方式來發布,並且必須是安全執行緒的或者由某個鎖保護起來。

  19. 在並發程式中使用和共用對象時,可以使用一些實用的策略,包括:

    • 線程封閉。線程封閉的對象只能由一個線程擁有,對象被封閉在該線程中,並且只能由這個線程修改。

    • 唯讀共用。在沒有額外同步的情況下,共用的唯讀對象可以由多個線程並發訪問,但任何線程都不能修改它。共用的唯讀對象包括不可變對象和事實不可變對象。

    • 安全執行緒共用。安全執行緒的對象在其內部實現同步,因此多個線程可以通過對象的公有介面來進行訪問而不需要進一步的同步。

    • 保護對象。被保護的對象只能通過持有特定的鎖來訪問。保護對象包括封裝在其他安全執行緒對象中的對象,以及發行的並且由某個特定鎖保護的對象。

體會

  1. 發布和逸出的理解:就是說一個類中的成員變數或者對象可以被其他的類所引用使用就是發布,如用static修飾的靜態變數或者是當前調用方法的對象。逸出是指該成員變數或對象在本來不應該被多線程引用的情況下暴露出去被引用,導致其值可能被錯誤修改的問題。一句話,不要隨便擴大一個類以及內部使用成員變數和方法的範圍。這也是封裝應該考慮的問題。

  2. this逸出:即在構造方法的內部類中啟動另一個線程引用了這個對象,但是這時這個對象還沒有構造完成,可能會導致出乎意料的錯誤。解決方案是建立一個Factory 方法,然後將構造器設定成私人構造器。

  3. final修改的成員變數需要在構造器在構造器中初始化,否則對象執行個體化後這個成員變數不能賦值。final修飾的成員變數是引用對象時,這個對象的地址不能修改,但是這個對象的值是可以修改的。

  4. 安全發布一個對象的四種方式的理解,如A類中有B類的引用:

    • A的靜態初始化方法,如public static A a = new A(b);這樣的靜態工廠類中,引用B的時候初始化B。

    • A類中的B成員變數用volatile b或者是AtomicReferance b這樣修飾。

    • A類中的B成員變數用final B b這樣修飾。

    • A類中的方法使用到B的時候用synchronized(lock){B…}包圍。

  5. 事實不可變對象很簡單的理解就是技術上是可變的,但是在商務邏輯處理中是不會去修改的對象。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.