《Java併發程式設計的藝術》筆記一——併發程式設計中的概念.md
0.背景
最近重溫《併發程式設計的藝術》這本書,覺得裡面有些不錯的內容,打算擷取一部分作為筆記和大家共同學習。
併發程式設計的目的是讓程式執行的更快。下面是併發程式設計的一些關鍵詞:
1 上下文切換
即使是單核處理器也支援多執行緒執行程式碼,CPU通過給每一個執行緒分配CPU時間來實現這個機制。時間篇是CPU分配給各個執行緒的時間,因為通常時間片設定的很短,所以CPU通過不停的切換執行緒執行,給我們造成執行緒是同時執行的錯覺。
概念: CPU通過時間片分配演算法來迴圈執行任務,當前任務執行一個時間片之後,會切換到下一個任務。在切換前會儲存上一個任務的狀態,以便下次切換回這個任務時,可以再次載入這個任務的狀態。所以從從儲存到再載入的過程就是一次上下文切換。
實踐表明當併發累加操作不超過10萬數量級以上時,速度會比序列累加操作要慢。為什麼呢?這是因為執行緒有建立和上下文切換的開銷
2 如何減少上下文切換
減少上下文切換的方法有: 無鎖併發程式設計、CAS演算法、使用最少執行緒和使用協程。
使用最少執行緒。 避免創造不需要的執行緒。
無鎖併發程式設計。 當多執行緒競爭鎖時,會進行多執行緒切換,所以多執行緒處理資料時,可以使用一些方法減少使用鎖。比如,將資料的ID按照Hash演算法取模分段,不同的執行緒處理不同的資料。
CAS演算法。CAS(比較與交換,Compare and swap) 是一種有名的無鎖演算法。CAS的語義是“我認為V的值應該為A,如果是,那麼將V的值更新為B,否則不修改並告訴V的值實際為多少”,CAS是一種 樂觀鎖 技術,當多個執行緒嘗試使用CAS同時更新同一個變數時,只有其中一個執行緒能更新變數的值,而其它執行緒都失敗,失敗的執行緒並不會被掛起,而是被告知這次競爭中失敗,並可以再次嘗試。CAS有3個運算元,記憶體值V,舊的預期值A,要修改的新值B。當且僅當預期值A和記憶體值V相同時,將記憶體值V修改為B,否則什麼都不做。
CAS是以原子操作為基礎,採用事務->提交->提交失敗->重試這樣特定程式設計手法的機制,它使得正在訪問共享資源的執行緒不依賴於任何其它執行緒的排程和執行,並且能夠在有限的步驟內完成。Java的Atomic包使用CAS演算法來更新資料,而不需要加鎖。協程。 在單執行緒裡實現多工的排程,並在擔心愛你成裡維持多個任務之間的切換。
3 死鎖
概念: 是指兩個或兩個以上的程序在執行過程中,由於競爭資源或者由於彼此通訊而造成的一種阻塞的現象,若無外力作用,它們都將無法推進下去。此時稱系統處於死鎖狀態或系統產生了死鎖,這些永遠在互相等待的程序稱為死鎖程序。
3.1產生死鎖的4個必要條件:
1)互斥條件:指程序對所分配到的資源進行排它性使用,即在一段時間內某資源只由一個程序佔用。如果此時還有其它程序請求資源,則請求者只能等待,直至佔有資源的程序用畢釋放。
2)請求和保持條件:指程序已經保持至少一個資源,但又提出了新的資源請求,而該資源已被其它程序佔有,此時請求程序阻塞,但又對自己已獲得的其它資源保持不放。
3)不剝奪條件:指程序已獲得的資源,在未使用完之前,不能被剝奪,只能在使用完時由自己釋放。
4)環路等待條件:指在發生死鎖時,必然存在一個程序——資源的環形鏈,即程序集合{P0,P1,P2,···,Pn}中的P0正在等待一個P1佔用的資源;P1正在等待P2佔用的資源,……,Pn正在等待已被P0佔用的資源。
3.2 預防和避免死鎖
在系統中已經出現死鎖後,應該及時檢測到死鎖的發生,並採取適當的措施來解除死鎖。當然最好的做法是預防和避免死鎖。
1)預防死鎖:通過設定某些限制條件,去破壞產生死鎖的四個必要條件中的一個或者幾個,來預防發生死鎖。預防死鎖是一種較易實現的方法,已被廣泛使用但是由於所施加的限制條件往往太嚴格,可能會導致系統資源利用率和系統吞吐量降低。
2) 避免死鎖。
該方法同樣是屬於事先預防的策略,但它並不須事先採取各種限制措施去破壞產生死鎖的的四個必要條件,而是在資源的動態分配過程中,用某種方法去防止系統進入不安全狀態,從而避免發生死鎖。
避免死鎖的常見方法:
避免一個執行緒同時獲取多個鎖
避免一個執行緒在鎖內同時佔用多個資源,儘量保證每個鎖只佔用一個資源。
嘗試使用定時鎖,使用lock.tryKock(timeout) 來替代使用內部鎖機制。
對於資料庫鎖,枷鎖和你解鎖必須在一個數據庫連結裡,否則會出現解鎖失敗的情況。
注1:CAS(比較與交換,Compare and swap) 是一種有名的無鎖演算法。CAS的語義是“我認為V的值應該為A,如果是,那麼將V的值更新為B,否則不修改並告訴V的值實際為多少”,CAS是一種 樂觀鎖 技術,當多個執行緒嘗試使用CAS同時更新同一個變數時,只有其中一個執行緒能更新變數的值,而其它執行緒都失敗,失敗的執行緒並不會被掛起,而是被告知這次競爭中失敗,並可以再次嘗試。CAS有3個運算元,記憶體值V,舊的預期值A,要修改的新值B。當且僅當預期值A和記憶體值V相同時,將記憶體值V修改為B,否則什麼都不做。
CAS是以原子操作為基礎,採用事務->提交->提交失敗->重試這樣特定程式設計手法的機制,它使得正在訪問共享資源的執行緒不依賴於任何其它執行緒的排程和執行,並且能夠在有限的步驟內完成。