并發程式設計無論在哪個伺服器端語言中都是位居進階進階位置。
1.并發程式設計面臨的問題?
如何解決好 ,分工 、同步(協作)、互斥?
1.1分工
分工就涉及到業務的拆分,這個和我們做項目一樣,先進性 需求分析,劃分子產品,拆任務、評估工期,然後 将任務配置設定 到個人手裡面(線程手裡)。
分工完畢後,我們常用多線程、線程池、fork/join方式處理可以并行進行的任務。
1.2 同步
在項目的開發中,具體的任務之間可能有依賴呢,例如你需要調用你同僚的某個接口提供服務,等到你用到的時候,可以先等着他把具體的接口定義定義完畢後,然後再進行後續的開發工作。
兩個開發人員(線程)可能産生依賴關系,開發過程中,可能發現 A需要 B提供的某個接口才能繼續程式設計,這樣就需要 B先進行接口定義,定義完畢後,通知A,A繼續進行目前業務開發處理。
各種工具類: CountDownLatch 、 Exchanger 、CycileBarrier 等等。
1.3 互斥
公司為了節省成本隻有一個UI設計人員(服務于多個項目),有兩個項目同時需要 UI人員進行圖形設計,同一時刻這個 UI開發人員,隻能完成 其中一個 項目的圖檔任務。這就有了競争,一般情況下
會根據項目優先級進行排期,優先級低的項目就隻能(阻塞)等待着優先級高的處理完畢了,再進行。
互斥一般的實作手段都是加鎖
從作業系統原理層面上來講,我了解了兩種 1、信号量 ; 2 、管程
2.信号量 和管程
2.1 信号量
為什麼要先聊信号量呢?因為這個先出現的。 信号量機制是由大名鼎鼎的荷蘭計算機科學家 Dijkstra 于1965 年提出。
信号量 模型 粗略描述
兩個成員變量:
1. 整型數 count:用于記錄共享資源數量 可以參考 Java 的 Semaphore,這個 初始值 count=2 表示 ,可以同時容忍兩個線程進入操作某個資源。
2. 等待隊列
- 兩個基本方法:
- P() 操作:将 count - 1,若 count < 0 時,線程進入等待隊列(Prolaag 荷蘭語,嘗試減少) 例如:初始化count=2, 調用兩次 P()後,count=0; 再次調用的時候就得放入等待隊列了,然後阻塞了。
- V() 操作:将 count + 1,若 count <= 0 時,喚醒一個等待隊列中的線程(Verhoog 荷蘭語,增加)
對信号量模型需要注意以下幾點:
- 初始化信号量時,我們指定 count 的值,之後 count 值的變化隻能由 P() / V() 進行操作
- 由作業系統實作并保證 P() / V() 操作的原子性
- P() 操作是可能阻塞的,V() 操作不會阻塞
- P() / V() 操作必須成對出現
信号量機制相比于鎖機制的一個主要差別是:信号量機制允許多個線程同時通路一個臨界區
因為隻要 count 符合條件,那麼線程就可以通路臨界區資源
信号量機制也存在一些缺點,那就是真正使用起來比較困難,因為當我們需要解決的同步問題越複雜,條件越多,那麼需要的信号量就越多,需要更加謹慎地處理信号量之間的處理順序否則很容易造成死鎖現象。
2.2 管程
JDK的synchornized 就是用的管程實作的。
如果扒開 位元組碼分析 可以看到代碼裡面多了
管程的 組成如下:
- 共享變量
- 入口等待隊列
- 一個鎖:控制整個管程代碼的互斥通路
- 0個或多個條件變量:每個條件變量都包含一個自己的等待隊列,以及相應的出/入隊操作。 最初 我們 用的最多的就是 wait 和notifyAll 了。
注意:在入口等待隊列中的線程,都是條件滿足情況未知或曾經滿足過條件的線程,它們等待管程的新一輪執行,而條件變量的等待隊列中的線程必須等到滿足該條件後才可以重新回到入口等待隊列中去
管程模型相較于信号量模型的好處就是:易用,它可以把多個同步條件的操作都集中在一個子產品裡,進而簡化同步機制的實作難度
參考
《極客時間》并發程式設計
https://zhuanlan.zhihu.com/p/150573153