互斥鎖
共享資源的使用是互斥的,即一個線程獲得資源的使用權后就會將改資源加鎖,使用完后會將其解鎖,所以在使用過程中有其它線程想要獲取該資源的鎖,那么它就會被阻塞陷入睡眠狀態,直到該資源被解鎖才會別喚醒,如果被阻塞的資源不止一個,那么它們都會被喚醒,但是獲得資源使用權的是第一個被喚醒的線程,其它線程又陷入沉睡。
遞歸鎖
同一個線程可以多次獲得該資源鎖,別的線程必須等待該線程釋放所有次數的鎖才能獲得。
讀寫鎖
讀寫鎖擁有讀狀態加鎖、寫狀態加鎖、不加鎖三種狀態。只有一個線程可以占有寫狀態的鎖,但可以多個線程同時占有讀狀態鎖,這也是它可以實現高並發的原因。當其處於寫狀態鎖下,任何想要嘗試獲得鎖的線程都會被阻塞,直到寫狀態鎖被釋放;如果是處於讀狀態鎖下,允許其它線程獲得它的讀狀態鎖,但是不允許獲得它的寫狀態鎖,當讀寫鎖感知到有線程想要獲得寫狀態鎖時,便會阻塞其后所有想要獲得讀狀態鎖的線程。所以讀寫鎖非常適合資源的讀操作遠多於寫操作的情況。
讀寫鎖三個特征:
- 多個讀者可以同時進行讀
- 寫者必須互斥,只允許一個寫者寫,也不能讀者寫者同時進行
- 寫者優先於讀者,一旦有寫者,則后續讀者必須等待,喚醒時優先考慮寫者
自旋鎖
自旋鎖是一種特殊的互斥鎖,當資源被加鎖后,其它線程想要再次加鎖,此時該線程不會被阻塞睡眠而是陷入循環等待狀態(不能再做其它事情),循環檢查資源持有者是否已經釋放了資源,這樣做的好處是減少了線程從睡眠到喚醒的資源消耗,但會一直占用CPU資源。適用於資源的鎖被持有的時間短,而不希望在線程的喚醒上花費太多資源的情況。
自旋鎖的目的
自旋鎖的實現是為了保護一段短小的臨界區操作代碼,保證這個臨界區的操作是原子的,從而避免並發的競爭冒險。在Linux內核中,自旋鎖通常用於包含內核數據結構的操作,你可以看到許多內核數據結構中都嵌入有spinlock,這些大部分就是用於保護它自身被操作的原子性,在操作這樣的結構體時都經歷這樣的過程:上鎖-操作-解鎖。
如果內核控制路徑發現自旋鎖“開着”(可以獲取),就獲取並繼續自己的執行。相反,如果內核控制路徑發現鎖由運行在另一個CPU上的內核控制路徑“鎖着”,就在原地“旋轉”,反復執行一條緊湊的循環檢測指令,直到鎖被被釋放。自旋鎖是循環檢測“忙等”,即等待時內核無事可做,進程在CPU上保持運行,所以它的臨界區必須小,且操作過程必須短。不過,自旋鎖通常非常方便,因為很多內核資源只鎖1毫秒的時間片段,所以等待自旋鎖的釋放不會消耗太多CPU的時間。