鎖從宏觀上分爲悲觀鎖與樂觀鎖

樂觀鎖

樂觀鎖是一種樂觀思想,即認爲讀多寫少,遇到併發寫的可能性低,每次去拿數據的時候都認爲別人不會修改,所以不會上鎖,但是在更新的時候會判斷一下在此期間別人有沒有去更新這個數據,採取在寫時先讀出當前版本號,然後加鎖操作(比較跟上一次的版本號,如果一樣則更新),如果失敗則要重複讀-比較-寫的操作。
  java中的樂觀鎖基本都是通過CAS操作實現的,CAS是一種更新的原子操作,比較當前值跟傳入值是否一樣,一樣則更新,否則失敗。

悲觀鎖

悲觀鎖是就是悲觀思想,即認爲寫多,遇到併發寫的可能性高,每次去拿數據的時候都認爲別人會修改,所以每次在讀寫數據的時候都會上鎖,這樣別人想讀寫這個數據就會block直到拿到鎖。java中的悲觀鎖就是Synchronized

公平鎖/非公平鎖

公平鎖和非公平鎖的區別就在於,公平鎖是FIFO機制,誰先來誰就在隊列的前面,就能優先獲得鎖。非公平鎖支持搶佔模式,先來的不一定能得到鎖(ReentrantLock)

爲什麼需要非公平鎖,因爲線程從阻塞到被喚醒是需要代價的

    java的線程是映射到操作系統原生線程之上的,如果要阻塞或喚醒一個線程就需要操作系統介入,
    需要在戶態與核心態之間切換,這種切換會消耗大量的系統資源,因爲用戶態與內核態
    都有各自專用的內存空間,專用的寄存器等,用戶態切換至內核態需要傳遞給許多變量、
    參數給內核,內核也需要保護好用戶態在切換時的一些寄存器值、變量等,以便內核態
    調用結束後切換回用戶態繼續工作。

    如果線程狀態切換是一個高頻操作時,這將會消耗很多CPU處理時間; 
    如果對於那些需要同步的簡單的代碼塊,獲取鎖掛起操作消耗的時間比用戶代碼執行的時間還要長,這種同步策略就會變得很吃性能

重入鎖

某線程獲取到鎖之後,再次獲取鎖的時候,不會產生阻塞,而是會將鎖的數量+1,線程釋放鎖的時候-1,直到爲0鎖完全釋放,由下一個線程嘗試獲取。(ReentrantLock)

由重量級分爲自旋鎖、輕量鎖、偏向鎖、重量級鎖。

自旋鎖

自旋鎖就是讓該線程等待一段時間,不會被立即掛起,如果持有鎖的線程很快就能釋放鎖,那麼該線程就可以直接獲取鎖;如果執行了幾個空循環之後還是不能獲取鎖,纔會被掛起
使用自旋鎖後,線程被掛起的機率相對減少,線程執行的連貫性相對加強。因此,對於那些鎖競爭不是很激烈,鎖佔用時間很短的併發線程,具有一定的積極意義;但對於鎖競爭激烈,單線程鎖佔用很長時間的併發程序,自旋鎖在自旋等待後,往往毅然無法獲得對應的鎖,不僅僅白白浪費了CPU時間,最終還是免不了被掛起的操作 ,反而浪費了系統的資源

偏向鎖

在單線程(非併發情況,即沒有競爭鎖的時候)訪問同步代碼塊的時候,可以忽略同步鎖機制,來提升性能

輕量級鎖

具體表現爲自旋鎖,如果自旋結束還是沒有獲取鎖,那麼升級成重量級鎖,線程阻塞,等待被喚醒

重量級鎖

重量級鎖在操作過程中,線程可能會被操作系統層面掛起,如果是這樣,線程間的切換和調用成本就會大大提高

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章