
本文介紹并發編程中的若干概念,實際上在筆者之前的文章中,已經介紹過很多概念。比如:并發與并行、同步與異步、鎖與信號量等等。參考[《并發編程專欄》](https://mp.weixin.qq.com/mp/appmsgalbum?__biz=MzU0NDU5MTk1MQ==&action=getalbum&album_id=1576334194996232194#wechat_redirect),本文計息介紹一些相對深入一些的概念
## 一、臨界區
為了方便大家理解,我們先看下面的這樣一張圖,我們可以把房子看作一個進程,每個房子里面的住戶及其活動看作一個線程,飲水機、健身器材、廁所都屬于共享資源。**這里的共享資源實際就是臨界區的概念**,臨界區的資源在同一時間只能被一個線程(住戶)使用,所以一旦臨界資源被占用,其他的線程(住戶)能做的就只有等待。

比如,在一個出租房內,住戶A占用了廁所,在他使用廁所的這段時間內廁所這個資源就是他獨占的。如果住戶B此時也想上廁所,就只能等待住戶A上完廁所之后才可以繼續使用該資源。
## 二、阻塞和非阻塞
了解了臨界區的概念之后,阻塞概念就好理解了。一個線程先占用了臨界區的資源,此時如果其他的線程想使用臨界區資源就必須等待。**這種占用臨界區資源,阻塞其他線程繼續執行的情況就是線程阻塞**(Blocking)。
然而**說到非阻塞,一般說的就是是否對當前線程自己產生阻塞**,比如:
* 我執行一個任務,比如使用飲水機接水。我拿了一個杯子接水,而我必須在飲水機前面等著水接完,這種就是阻塞式線程。
* 如果我拿了杯子接水,把杯子放到飲水機下面,飲水機會在杯子接滿水之后,自動對我發出異步通知(比如聲音告警)。我**可以在此期間做其他的事情,這種就是非阻塞式線程(Non-Blocking)**。非阻塞式線程,從編程的角度一般都是通過回調函數,或者響應式編程(Reactive programming)實現的。
## 三、死鎖、饑餓和活鎖
* 死鎖:我們來看上面的這張圖,在十字路口A車道的A1車像轉向B車道,但B車道入口被B1車占用;在十字路口B車道的B1車像轉向C車道,但C車道入口被C1車占用;在十字路口C車道的C1車像轉向D車道,但D車道入口被D1車占用;在十字路口D車道的D1車像轉向A車道,但A車道入口被A1車占用;也就是說:**線程因為資源競爭,彼此需要資源又都無法釋放,導致線程無法獲取下一步執行所需的資源,導致死鎖產生。**

* 饑餓:舉個例子農民給小雞喂食,如果有五只雞每次都剛好給五只雞的飼料量。無法避免的是有的雞吃的量超過規劃量,最總導致某一只雞無法吃到足夠的飼料。由于它無法遲到足夠的飼料,它就比較瘦弱,搶食的能力弱。惡性循環,最終很可能被餓死。具體到編程層面,就是**某一線程A的優先級比較低,然后優先級高的線程又經常占用資源不釋放,線程A長期無法得到有效執行,處于饑餓狀態**。極端情況下,可能被餓死。
* 活鎖:相對于死鎖和饑餓,活鎖是一種相對好的狀態。大家在生活中肯定遇到過這樣一種情況,你在樓梯拐角遇到一個同事,空間有限所以二人茬住了。**你向左移動,你的同事也向左移動;你向右移動,你的同事也向右移動;所以你們兩個人都無法向前移動,這就是一個典型的活鎖**。因為人是高智慧的動物,又都懂得禮讓,所以對于人來說活鎖的問題很好解決,只要其中一個人原地不動,另一個人動一下就過去了。但是多線程面對活鎖的時候就沒有那么智能了,有可能出現不斷地釋放資源(向左移)、占用資源(向右移)的循環中,即使最終活鎖被解開,其資源開銷及時間成本都是很大的。
- 線程
- 1.進程和線程-鎖與信號量
- 2.Thread類線程狀態轉換
- 2.并發與并行-同步與異步
- 4.線程池
- 5.對象級別與類級別的同步鎖
- 6.創建線程的四種方式
- 7.臨界區-阻塞-活鎖-死鎖
- 2.JMM多線程模型
- JUC
- BlockingQueue
- ArrayBlockingQueue
- DelayQueue
- LinkedBlockingQueue
- PriorityBlockingQueue
- SynchronousQueue
- BlockingDeque
- ConcurrentHashMap
- CountDownLatch
- CyclicBarrier
- Exchanger
- AtomicInteger
- Lock
- Condition
- ReentrantLock讀寫鎖
- StampedLock
- Semaphore