1. 程式人生 > >java並發編程實戰--讀後總結

java並發編程實戰--讀後總結

大小 簡單的 5.1 定性 運行速度 並行 程序 計時 fix

1.簡介

  1.1並發簡史

    產生原因:資源利用率、公平性、便利性

  1.2線程的優勢

    1.2.1發揮多處理器的強大功能

    1.2.2建模的簡單性

    1.2.3異步事件的簡化處理

    1.2.4響應更靈敏的用戶界面

  1.3線程帶來的風行

    1.3.1安全性問題

      永遠不發生糟糕的事情

    1.3.2活躍性問題

      某件正確的事情最終會發生

    1.3.3性能問題

      希望正確的事情盡快發生

  1.4線程無處不在

    所使用的框架可能會創建線程

    Timer類,用於延遲運行任務一次或多次,確保訪問對象是線程安全的

    Servlet和JSP,需要保證多個Servlet共享對象的線程安全

    遠程方法調用

    Swing和AWT

2.線程安全性

  2.1什麽是線程安全性

    多個線程訪問某個類時,這個類始終都能表現出正確的行為,這個類是線程安全的

    無狀態對象一定是線程安全的

  2.2原子性

    2.2.1競態條件

      當計算的正確性取決於線程的執行時序時,就會發生竟態條件

      基於失效的觀察結果做出判斷的竟態條件稱為“先檢查後執行”

    2.2.2示例:延遲初始化中的競態條件

    2.2.3復合操作

      將“先檢查後執行”和“讀取-修改-寫入”等操作統稱為復合操作

  2.3加鎖機制

    2.3.1內置鎖

    2.3.2重入

  2.4用鎖來保護狀態

  2.5活躍性與性能

    當執行時間較長的計算或者可能無法快速完成的操作時,一定不要持有鎖

3.對象的共享

  3.1可見性

    3.1.1失效數據

      獲取到一個未更新的值

    3.1.2非原子的64位操作

      獲取的失效數據是之前的值而非隨機值是最低安全性,但不適用於非volaties類型的64位數值變量,因為JVM會把64位的讀操作和寫操作分解為2個32位的操作

      在多線程中使用共享且可變的long和double等類型的變量是不安全的,除非用volatie聲明或鎖保護

    3.1.3加鎖與可見性

    3.1.4Volatile變量

      Volatile聲明的變Volatile變量量是共享的,讀取Volatile變量總能獲取最新的值

      但是Volatile變量比較脆弱,僅當Volatile變量能簡化代碼的失效以及對同步策略的驗證時才應該使用它們

      加鎖機制可以確保可見性和原子性,Volatile變量只能確保可見性

  3.2發布與逸出

    發布:使對象能夠在當前作用域之外的代碼中使用

    逸出:不應該被發布的對象被發布時稱為逸出

    安全的對象構造過程

      不要在構造過程使用this逸出

      使用工廠來放在this引用在構造過程中逸出

  3.3線程封閉

    3.3.1Ad-hoc線程封閉

      維護線程封閉性的職責完全由程序實現,但是具有脆弱性,盡量少用

    3.3.2棧封閉

    3.3.3ThreadLocal類

      使線程中的值與保存值的對象關聯起來,總能獲取到最新值

      ThreadLocal變量類似於全局變量,降低了代碼的可重用性,在類之間引入了隱含的耦合性

  3.4不變性

    3.4.1Final域

    3.4.2示例:使用Volatile類型來發布不可變對象

  3.5安全發布

    3.5.1不正確的發布:正確的對象被破壞

    3.5.2不口不對象與初始化安全性

    3.5.3安全發布的常用模式

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

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

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

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

    3.5.4事實不可變對象

      對象在技術上看是可變,但是其狀態在發布後不會再改變的對象稱為“事實不可變對象”

    3.5.5可變對象

      不可變對象可以任意發布

      事實不可變對象通過安全方式發布

      可變對象通過安全方式發布,並且必須是線程安全的或者鎖保護起來

    3.5.6安全的共享對象

      策略:線程封閉、只讀共享、線程安全共享、保護對象 

4.對象的組合

  4.1設計線程安全的類

    4.1.1收集同步需求

    4.1.2依賴狀態的操作

    4.1.3狀態的所有權

  4.2實例封閉

    4.2.1Java監視器模式

    4.2.2實例:車輛追蹤

  4.3線程安全性的委托

    4.3.1示例:基於委托的車輛追蹤器

    4.3.2獨立的狀態變量

    4.3.3當委托失效時

    4.3.4發布底層的狀態變量

    4.3.5示例:發布狀態的車輛追蹤器

  4.4在現有的線程安全類中添加功能

    4.4.1客戶端加鎖機制

      對使用某個對象X的客戶端代碼,使用X本身用於保護其狀態的鎖來保護這段客戶端代碼

      若沒有則添加的正確的輔助類

public class ListHelper<E> {
    public List<E> list = Collections.synchronizedList(new ArrayList<>());

    public boolean putIfAbsent(E x) {
        synchronized (list) {//註意加鎖的對象
            boolean absent = !list.contains(x);
            if (absent) {
                list.add(x);
            }
            return absent;
        }
    }
}

      會破壞同步策略的封裝性

    4.4.2組合

public class ImprovedList<T> implements List<T> {
    private final List<T> list;

    public ImprovedList(List<T> list) {
        this.list = list;
    }

    public synchronized boolean putIfAbsent(T x) {
        boolean contains = list.contains(x);
        if (contains) {
            list.add(x);
        }
        return !contains;
    }
}

  4.5將同步策略文檔化

    解釋含糊的文檔

5.基礎構建模塊

  5.1同步容器類

    5.1.1同步容器類的問題

    5.1.2叠代器與Concurrent-ModificationException

    5.1.3隱藏叠代器

  5.2並發容器

    5.2.1ConcurrentHashMap

    5.2.2額外的原子Map操作

    5.2.3CopyOnWriteArrayList

      當叠代操作遠遠多於修改操作時才應該使用“寫入時復制”容器

  5.3阻塞隊列和生產者-消費模式

    5.3.1示例:桌面搜索

    5.3.2串行線程封閉

    5.3.3雙端隊列與工作密取

  5.4阻塞方法與中斷方法

    恢復中斷

  5.5同步工具類

    5.5.1閉鎖

      延遲線程的進度直到其到達終止狀態,用於確保某些活動直到其他活動都完成才執行

public class TestHarness {
    public long timeTasks(int nThrads, final Runnable task) throws InterruptedException {
        final CountDownLatch startGate = new CountDownLatch(1);//起始門
        final CountDownLatch endGate = new CountDownLatch(nThrads);//結束門
        for (int i = 0; i < nThrads; i++) {
            Thread t = new Thread() {
                public void run() {
                    try {
                        startGate.await();//等待所有線程準備就緒
                        try {
                            task.run();
                        } finally {
                            endGate.countDown();//完成一件事情就減1
                        }
                    } catch (InterruptedException ignored) {

                    }
                }
            };
            t.start();
        }
        long start = System.nanoTime();
        startGate.countDown();
        endGate.await();
        long end = System.nanoTime();
        return end - start;
    }
}

    5.5.2FutureTask

      用於閉鎖

    5.5.3信號量

      計數信號量用於控制同時訪問特定資源的操作數量

    5.5.4柵欄

      所有線程都必須同時到達柵欄才能繼續執行,柵欄可以重置復用

  5.6構建高效且可伸縮的結果緩存

6.任務執行

  6.1在線程中執行任務

    6.1.1串行地執行任務

    6.1.2顯示地為任務創建線程

    6.1.3無限制創建線程的不足

      線程生命周期的開銷非常高

      資源消耗大

      穩定性差

  6.2Executor框架

    6.2.1示例:基於Executor的Web服務器

public class Test {
    private static final int NTHREADS = 100;
    private static final Executor exec = Executors.newFixedThreadPool(NTHREADS);

    public static void main(String[] args) throws IOException {
        ServerSocket socket = new ServerSocket(8080);
        while (true) {
            final Socket connection = socket.accept();
            Runnable task = new Runnable() {
                public void run() {
                    handleRequest(connection);
                }
            };
            exec.execute(task);
        }
    }
}

    6.2.2執行策略

    6.2.3線程池

      newFixedThreadPool()創建固定長度的線程池

      newCachedThreadPool()創建可緩存的線程池,當前規模超過處理需求時,回收空閑線程,需求增加時,添加新線程,規模大小無限制

      newScheduledThreadPool()以延遲或定時方式執行任務的固定線程池

    6.2.4Executor的生命周期

    6.2.5延遲任務與周期任務

      Timer類精準性有問題,而且拋出異常就會被取消

      newScheduledThreadPool是基於相對時間的,用於間隔時間的執行的任務,例如1分鐘檢查一次活動

  6.3找出可利用的並行性

    6.3.1示例:串行的頁面渲染器

    6.3.2攜帶結果的任務Callable與Future

    6.3.3示例使用Future實現頁面渲染器

    6.3.4在異構任務並行化中存在的局限

    6.3.5CompletionService:Executor與BlockingQueue

    6.3.6示例:使用CompletionService實現頁面渲染器

    6.3.7為任務設置時限

    6.3.8示例:旅行預定門戶網站

      invokeAll()可以將按照任務集合中叠代器的順序將所有Future添加到返回的集合中,當所有任務執行完畢或中斷或超時,invokeAll將返回

7.取消與關閉

  7.1任務取消

    7.1.1中斷

      用戶請求取消

      有時間限制的操作,搜索任務超時,取消正在搜索的任務

      應用程序事件,解決方法找到時,取消其他搜索解決方案的任務

      錯誤

      關閉,程序或服務器關閉

      發出中斷請求,線程在合適的時刻中斷自己

    7.1.2中斷策略  

    7.1.3響應中斷

    7.1.4示例:計時運行

    7.1.5通過Future來實現取消

    7.1.6處理不可中斷的阻塞

      Java.io包中的同步Socket I/O

      Java.io包中的同步I/O

      Selector的異步I/O

      獲取某個鎖

    7.1.7才有newTaskFor來封裝非標準的取消

  7.2停止基於線程的服務

    7.2.1示例:日誌服務

    7.2.2關閉ExecutorService

    7.2.3“毒丸”對象

      當隊列得到這個“毒丸”對象,立即停止

    7.2.4示例:只執行一次的服務

    boolean checkMail(Set<String> hosts, long timeout, TimeUnit unit) throws InterruptedException {
        ExecutorService exec = Executors.newCachedThreadPool();
        final AtomicBoolean hasNewMail = new AtomicBoolean((false));
        try {
            for (final String host : hosts) {
                exec.execute(new Runnable() {
                    public void run() {
                        if (checkMail(host)) {
                            hasNewMail.set(true);
                        }
                    }
                });
            }
        } finally {
            exec.shutdown();//安全的關閉
            exec.awaitTermination(timeout, unit);
        }
        return hasNewMail.get();
    }

    7.2.5shuadownNow的局限性

  7.3處理非正常的線程終止

    未捕獲一次的處理

  7.4JVM關閉

    7.4.1關閉鉤子

      通過Runtime.addShutdownHook註冊的但尚未開始的線程

      用於實現服務或應用程序的清理工作

    7.4.2守護線程

      主線程創建的所有線程都是普通線程

      主線程創建之外的所有線程都是守護線程

    7.4.3終結器

8.線程池的使用

  8.1在任務與執行處理之間的耦合

    8.1.1線程饑餓死鎖

      一個線程無期限的等待其他線程的資源或條件會發送線程饑餓死鎖

    8.1.2運行時間較長的任務

  8.2設置線程池的大小

  8.3配置ThreadPoolExecutor

    8.3.1線程的創建與銷毀

    8.3.2管理隊列任務

    8.3.3飽和策略

      “中止”策略,拋棄下一個將要被執行的任務

      “調用者運行”策略,將任務回退給調用者

    8.3.4線程工廠

    8.3.5在調用構造函數後再定制ThreadPoolExecutor

  8.4擴展ThreadPoolExecutor

    示例:給線程池添加統計信息

  8.5遞歸算法的並行化

    示例:謎題框架

9.圖形用戶界面應用程序

  9.1為什麽GUI是多線程的

    9.1.1串行事件處理

    9.1.2Swing中的線程封閉機制

  9.2短時間的GUI任務

  9.3長時間的GUI任務

    9.3.1取消

    9.3.2進度標識和完成標識

    9.3.3SwingWorker

  9.4共享數據模型

    9.4.1線程安全的數據模型

    9.4.2分解數據模型

  9.5其他形式的單線程子系統

10.避免活躍性危險

  10.1死鎖

    10.1.1鎖順序死鎖

    10.1.2動態的鎖順序死鎖

    10.1.3在協作對象之間發送的死鎖

    10.1.4開放調用

      調用方法時不持有鎖,這種調用稱為開放調用

    10.1.5資源死鎖

  10.2死鎖的避免與診斷

    10.2.1支持定時的鎖

    10.2.2通過線程轉儲信息來分析死鎖

  10.3其他活躍性危險

    10.3.1饑餓

      要避免使用線程優先級,會增加平臺依賴性

    10.3.2糟糕的響應性

    10.3.3活鎖

      多個相互協作的線程對彼此進行響應從而修改各自的狀態,使得任何一個線程都無法繼續執行時,就發生了活鎖

      通過等待隨機長度的時間和回退可以有效的避免活鎖的發生

11.性能與可伸縮性

  11.1對性能的思考

    11.1.1性能與可伸縮性

      運行速度指標:服務器時間、等待時間

      處理能力指標:生產量、吞吐量

      可伸縮性:增加計算資源時,程序的吞吐量或者處理能力相應地增加

    11.1.2評估各種性能權衡因素

  11.2Amdahl定律

    11.2.1示例:在各種框架中隱藏的串行部分

    11.2.2Amdahl定律的應用

      準確估計出執行過程中串行部分所占的比例

  11.3線程引入的開銷

    11.3.1上下文切換

      可運行的線程大於CPU數量時,保存當前運行線程的執行上下文,新調度進來的線程執行上下文設置為當前上下文

    11.3.2內存同步

    11.3.3阻塞

  11.4減少鎖的競爭

    11.4.1縮小鎖的範圍("快進快出")

    11.4.2減小鎖的粒度

    11.4.3鎖分段

    11.4.4避免熱點域

    11.4.5一些替代獨占鎖的方法

    11.4.6監測CPU的利用率

      負載不充足

      I/O密集

      外部限制

      鎖競爭

    11.4.7向對象池說“不”

  11.5示例:比較Map的性能

  11.6減少上下文切換的開銷

12.並發程序的測試

  12.1正確性測試

    12.1.1基本的單元測試

    12.1.2對阻塞操作的測試

    12.1.3安全性測試

    12.1.4資源管理的測試

    12.1.5使用回調

    12.1.6產生更多的交替操作

  12.2性能測試

    12.2.1在PutTakeTest增加計時功能

    12.2.2多種算法的比較

    12.2.3響應性衡量

  12.3避免性能測試的陷阱

    12.3.1垃圾回收

    12.3.2動態編譯

    12.3.3對代碼路徑的不真實采樣

    12.3.4不真實的競爭程度

    12.3.5無用代碼的清除

  12.4其他的測試方法

    12.4.1代碼審查

    12.4.2靜態分析工具

    12.4.3面向方面的測試技術

    12.4.4分析與監測工具

13.顯示鎖

  13.1Lock與ReentrantLock

    13.1.1輪詢鎖與定時鎖

    13.1.2可中斷的鎖獲取操作

    13.1.3非塊結構的加鎖

  13.2性能考慮因素

  13.3公平性

  13.4在synchronized和ReentrantLock之間進行宣傳

  13.5讀-寫鎖

14.構建自定義的同步工具

  14.1狀態依賴性的管理

    14.1.1示例:將前提條件的失敗傳遞給調用者

    14.1.2示例:通過輪詢與休眠來實現簡單的阻塞

    14.1.3條件隊列

  14.2使用條件隊列

    14.2.1條件謂詞

    14.2.2過早喚醒

    14.2.3丟失的信號

    14.2.4通知

    14.2.5示例:閥門類

    14.2.6子類的安全問題

    14.2.7封裝條件隊列

    14.2.8入口協議與出口協議

  14.3顯示的Condition對象

  14.4Synchronized剖析

  14.5AbstractQueuedSynchronizer

  14.6java.util.concurrent同步器類的AQS

    14.6.1ReentrantLock

    14.6.2Semaphore與CountDownLatch

    14.6.3FutureTask

    14.6.4ReentrantReadWriteLock

15.原子變量與非阻塞同步機制

  15.1鎖的劣勢

  15.2硬件對並發的支持

    15.2.1比較並交換

    15.2.2非阻塞的計數器

    15.2.3JVM對CAS的支持

  15.3原子變量類

    15.3.1原子變量是一種更好的volatile

    15.3.2性能比較:鎖與原子變量

  15.4非阻塞算法

    15.4.1非阻塞的棧

    15.4.2非阻塞的鏈表

    15.4.3原子的域更新器

    15.4.4ABA問題

16.Java內存模型

  16.1什麽是內存模型,為什麽需要它

    16.1.1平臺的內存模型

    16.1.2重排序

    16.1.3Java內存模型簡介

    16.1.4借助同步

  16.2發布

    16.2.1不安全的發布

    16.2.2安全的發布

    16.2.3安全初始化模式

    16.2.4雙重檢查加鎖

  16.3初始化過程中的安全性

java並發編程實戰--讀後總結