java並發編程實踐筆記

來源:互聯網
上載者:User

1, 保證安全執行緒的三種方法 :

a, 不要跨線程訪問共用變數
b, 使共用變數是 final類型的
c, 將共用變數的操作加上同步

2, 一開始就將類設計成安全執行緒的 , 比在後期重新修複它 ,更容易 .

3, 編寫多線程程式 , 首先保證它是正確的 , 其次再考慮效能 .

4, 無狀態或唯讀對象永遠是安全執行緒的 .

5, 不要將一個共用變數裸露在多線程環境下 (無同步或不可變性保護 )

6, 多線程環境下的消極式載入需要同步的保護 , 因為消極式載入會造成對象重複執行個體化

7, 對於 volatile 聲明的數實值型別變數進行運算 , 往往是不安全的 (volatile 只能保證可見度 , 不能保證原子性 ).
詳見 volatile 原理與技巧中 , 髒資料問題討論 .

8, 當一個線程請求獲得它自己佔有的鎖時 ( 同一把鎖的嵌套使用 ), 我們稱該鎖為可重新進入鎖 .
在 jdk1.5 並發包中 , 提供了可重新進入鎖的 java 實現 -ReentrantLock.

9, 每個共用變數 , 都應該由一個唯一確定的鎖保護 .
建立與變數相同數目的 ReentrantLock, 使他們負責每個變數的安全執行緒 .

10,雖然縮小同步塊的範圍 , 可以提升系統效能 .
但在保證原子性的情況下 , 不可將原子操作分解成多個 synchronized塊 .

11, 在沒有同步的情況下 , 編譯器與處理器運行時的指令執行順序可能完全出乎意料 .
原因是 , 編譯器或處理器為了最佳化自身執行效率 , 而對指令進行了的重排序 (reordering).

12, 當一個線程在沒有同步的情況下讀取變數 , 它可能會得到一個到期值 , 但是至少它可以看到那個
線程在當時設定的一個真實數值 . 而不是憑空而來的值 . 這種安全保證 , 稱之為 最低限的安全性 (out-of-thin-air safety)

在開發並發應用程式時 , 有時為了大幅度提高系統的輸送量與效能 , 會採用這種無保障的做法 .
但是針對 , 數值的運算 , 仍舊是被否決的 .

13, volatile 變數 , 只能保證可見度 , 無法保證原子性 .

14, 某些耗時較長的網路操作或 IO, 確保執行時 , 不要佔有鎖 .

15, 發布 (publish) 對象 , 指的是使它能夠被當前範圍之外的代碼所使用 .( 引用傳遞 )
對象逸出 (escape), 指的是一個對象在尚未準備好時將它發布 .

原則 : 為防止逸出 , 對象必須要被完全構造完後 , 才可以被發布 ( 最好的解決方式是採用同步 )

this 關鍵字引用對象逸出
例子 : 在建構函式中 , 開啟線程 , 並將自身對象 this 傳入線程 , 造成引用傳遞 .
而此時 , 建構函式尚未執行完 , 就會發生對象逸出了 .

16, 必要時 , 使用 ThreadLocal變數確保線程封閉性 (封閉線程往往是比較安全的 , 但一定程度上會造成效能損耗 )
封閉對象的例子在實際使用過程中 , 比較常見 , 例如 hibernate openSessionInView機制 , jdbc的 connection機制 .

17, 單一不可變對象往往是安全執行緒的 (複雜不可變對象需要保證其內部成員變數也是不可變的 )
良好的多線程編程習慣是 : 將所有的域都聲明為 final, 除非它們是可變的

18, 保證共用變數的發布是安全的 
a, 通過靜態初始化器初始化對象 (jls 12.4.2 敘述 , jvm 會保證靜態初始設定變數是同步的 )
b, 將對象申明為 volatile 或使用 AtomicReference
c, 保證對象是不可變的
d, 將引用或可變操作都由鎖來保護

19, 設計安全執行緒的類 , 應該包括的基本要素 :
a, 確定哪些是可變共用變數
b, 確定哪些是不可變的變數
c, 指定一個管理並發訪問對象狀態的策略

20, 將資料封裝在對象內部 , 並保證對資料的訪問是原子的 .
建議採用 volatile javabean 模型或者構造同步的 getter,setter.

21, 線程限制性使構造安全執行緒的類變得更容易 , 因為類的狀態被限制後 , 分析它的執行緒安全性時 , 就不必檢查完整的程式 .

22, 編寫並發程式 , 需要更全的注釋 , 更完整的文檔說明 .

23, 在需要細分鎖的分配時 , 使用 java監視器模式好於使用自身對象的監視器鎖 .
前者的靈活性更好 .

Object target = new Object();
// 這裡使用外部對象來作為監視器 , 而非 this
synchronized(target) {
// TODO
}

針對 java monitor pattern, 實際上 ReentrantLock的實現更易於並發編程 .
功能上 , 也更強大 .

24, 設計並發程式時 , 在保證伸縮性與效能折中的前提下 , 優先考慮將共用變數委託給安全執行緒的類 .
由它來控制全域的並發訪問 .

25, 使用普通同步容器 (Vector, Hashtable) 的迭代器 , 需要外部鎖來保證其原子性 .
原因是 , 普通同步容器產生的迭代器是非安全執行緒的 .

26, 在並發編程中 , 需要容器支援的時候 , 優先考慮使用 jdk 並發容器 

(ConcurrentHashMap, ConcurrentLinkedQueue, CopyOnWriteArrayList…).

27, ConcurrentHashMap, CopyOnWriteArrayList
並發容器的迭代器 , 以及全範圍的 size(), isEmpty() 都表現出弱一致性 .
他們只能標示容器當時的一個資料狀態 . 無法完整響應容器之後的變化和修改 .

28, 使用有界隊列 , 在隊列充滿或為空白時 , 阻塞所有的讀與寫操作 . ( 實現生產 – 消費的良好方案 )
BlockQueue 下的實現有 LinkedBlockingQueue 與 ArrayBlockingQueue, 前者為鏈表 , 可變操作頻繁優先考慮 , 後者為數組 , 讀取操作頻繁優先考慮 .
PriorityBlockingQueue 是一個按優先順序順序排列的阻塞隊列 , 它可以對所有置入的元素進行排序 ( 實現 Comparator 介面 )

29, 當一個方法 , 能拋出 InterruptedException, 則意味著 , 這個方法是一個可阻塞的方法 , 如果它被中斷 , 將提前結束阻塞狀態 .
當你調用一個阻塞方法 , 也就意味著 , 本身也稱為了一個阻塞方法 , 因為你必須等待阻塞方法返回 .

如果阻塞方法拋出了中斷異常 , 我們需要做的是 , 將其往上層拋 , 除非當前已經是需要捕獲異常的層次 .
如果當前方法 , 不能拋出 InterruptedException, 可以使用 Thread.currentThread.interrupt() 方法 , 手動進行中斷 .

轉載請註明原文連結:http://kenwublog.com/java-concurrency-in-practise-note

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.