1. 程式人生 > >讀書筆記: 大型網站系統與Java中介軟體實踐(1)

讀書筆記: 大型網站系統與Java中介軟體實踐(1)

1 分散式系統簡介

本章主要對分散式系統做了一個大概的介紹,重點在於以下兩個小節。

1.1 執行緒的執行模式

(1) 無需通訊的多執行緒模式

執行緒之間不需要處理共享的資料,也不需要進行動作協調,這是最簡單的多執行緒執行模式。各個執行緒獨立處理自己的工作,不涉及執行緒安全。

(2) 基於共享容器協同的多執行緒模式

在一些場景中多個執行緒需要對共享資料進行處理,如經典的生產者-消費者模式。Producer生產的產品放在佇列中,Consumer執行緒併發地訪問這個佇列進行消費。

對於這種在多執行緒環境下對同一份資料的訪問,需要有保護機制以保證訪問的正確性。

儲存共享資料的容器或物件,有執行緒安全和不安全之分。對於執行緒不安全物件,可通過加鎖方式來控制併發訪問。對於執行緒安全的容器和物件(執行緒安全物件可參考博主的“

執行緒安全物件簡介”一文),可以在多執行緒環境下直接使用。

值得一提的是,並不是所有場景下使用執行緒安全容器就可以保證執行緒安全,要根據具體處理邏輯分析。舉個例子,假設有多個執行緒對共享變數Map中的count做+1操作。如果使用synchronize關鍵字,那麼沒有什麼問題。Code如下:

private static final Map<String, Integer> map = new HashMap<>();
//private static final ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();

public synchronized void add() {
    Integer value = map.get("count");
    if (value == null) {
        map.put("count", 1);
    } else {
        map.put("count", value + 1);
    }
}

@Test
public void testA() throws InterruptedException {
    for (int i = 0; i < 200; i++) {
        new Thread(this::add).start(); // java8的lambda表示式寫法
    }
    Thread.sleep(2000); // 主執行緒sleep讓子執行緒執行完
    System.out.println(map.get("count"));
}

如果不使用synchronize關鍵字,而是使用執行緒安全容器ConcurrentHashMap呢? 輸出的最終結果並不是預期的200,說明這段程式碼是執行緒不安全的。原因在於add方法中的邏輯:先讀後寫,這不是一個原子操作。使用同步鎖時,將整個add方法鎖住了,相當於將add裡面的邏輯變成了一個原子操作。而使用ConcurrentHashMap同時去掉synchronize之後,讀和寫就分離開了。假設初始值為0,A執行緒讀取值,對該值加1,然後cpu時間給到執行緒B,執行緒B做同樣的操作,並將1寫入。然後執行緒A恢復,同樣將1寫入。預期結果是2,實際結果卻是1。

這樣看來,ConcurrentHashMap好像並沒有起到執行緒安全的作用,那為什麼說它是執行緒安全的呢?事實上,ConcurrentHashMap的執行緒安全是相對於HashMap來說的,它的每個方法單獨呼叫(如put方法)是執行緒安全的(通過分段鎖來保證原子操作),但是使用ConcurrentHashMap的業務程式碼的執行緒安全性是需要開發者自己來保證的。而HashMap之所以執行緒不安全,是由於其方法單獨呼叫也是不安全的,如put方法,底層包含了很多其他邏輯,在執行put的底層邏輯時,隨時會被其他執行緒打斷。

(3)事件協同的多執行緒模式 除了對執行緒的併發訪問的控制之外,還存在著協調的需求。如哲學家進餐問題,如果沒有協調好,就會造成死鎖。

1.2 網路通訊基礎

網路模型;網路IO實現方式;