多線程中的鎖

樂觀鎖和悲觀鎖數據庫

樂觀鎖和悲觀鎖是在數據庫中引入的名詞,可是在併發包鎖裏面也引入了相似的思想,因此這裏仍是有必要講解一下。併發

  • 悲觀鎖指對數據被外界修改持保守的態度,認爲數據很容易就會被其餘線程修改,因此在數據被處理前就先對數據加鎖,並在整個數據處理過程當中,使數據處於鎖定狀態,悲觀鎖的實現每每依靠數據庫提供的鎖機制,即在數據庫中,在對數據記錄操做前給記錄加排它鎖,若是獲取鎖失敗,則說明數據正在被其餘線程修改,當前線程則等待或者拋出異常,若是獲取鎖成功,則對記錄進行操做,而後提交事務後釋放排它鎖
  • 樂觀鎖是相對於悲觀鎖來講的,它認爲數據在通常狀況下不會形成衝突,因此在訪問記錄前不會加派它鎖,而是在進行數據提交更新時,纔會正式對數據衝突與否進行檢測,具體來講,根據update返回的行數讓用戶決定如何去作

         樂觀鎖並不會使用數據庫提供的鎖機制,通常在表中添加version字段或者使用業務狀態來實現,樂觀鎖直到提交時才鎖定,因此不會產生任何死鎖。函數

公平鎖和非公平鎖性能

根據線程獲取鎖的搶佔機制,鎖能夠分爲公平鎖和非公平鎖,公平鎖表示線程獲取鎖的順序是按照線程的請求鎖的時間遲早來決定的,也便是最先請求鎖將最先得到鎖,而非公平鎖則在運行時間闖入,也就是先來不必定先得。spa

     ReetrantLock提供了公平和非公平鎖的實現操作系統

  • 公平鎖: ReetrantLock  pairLock=new ReetrantLock(true).
  • 非公平鎖: ReetrantLock  pairLock=new ReetrantLock(false),若是構造函數不傳參數,則默認是非公平鎖

     在沒有公平性需求的前提下儘可能使用非公平鎖,由於公平鎖會帶來性能開銷。線程

獨佔鎖與共享鎖事務

根據鎖只能被單個線程持有仍是能被多個線程共同持有,鎖能夠分爲獨佔鎖和共享鎖。資源

獨佔鎖保證任什麼時候候都只有一個線程能得到鎖,ReetrantLock就是以獨佔方式實現的。共享鎖則能夠同時由多個線程持有,例如ReadWriteLock讀寫鎖,它容許一個資源能夠被多個線程同時進行讀操做。it

獨佔鎖是一種悲觀鎖,因爲每次訪問資源都先加上互斥鎖,這限制了併發性,由於讀操做不會影響數據的一致性,而獨佔鎖只容許在同一個時間由一個線程讀取數據,其餘線程必須等待當前線程釋放鎖才能進行讀取。

共享鎖是一種樂觀鎖,它放寬了加鎖的條件,容許多個線程同時進行讀操做

可重入鎖

當一個線程要獲取一個被其餘線程持有的獨佔鎖時,該線程會阻塞,那麼當一個線程再次獲取它本身已經獲取的鎖時是否會阻塞呢?若是不被阻塞,那麼咱們說該鎖是可重入的,也便是隻要該線程獲取了鎖,那麼能夠無限次數地進入被該鎖鎖住的代碼塊。

實際上,synchronized內部鎖是可重入的,可重入鎖的原理是在鎖內部維護一個線程標示,用來標示該鎖被哪一個線程佔用,而後關聯一個計數器,一開始計數器值爲0,說明該鎖沒有被任何線程扎用,當一個線程獲取了該鎖時,計數器的值會變成1,這時其餘線程再來獲取該鎖的時會發現鎖的全部者不是本身而被阻塞掛起,可是當獲取了該鎖的線程再次獲取鎖時發現擁有者是本身,就會把計數器的值加1,當釋放鎖的時候計數器的值-1,當計數器的值爲0時,鎖裏面的線程標示被重置爲null,這個時候被阻塞的線程會被喚醒來競爭獲取該鎖。

自旋鎖

因爲Java中的線程是與操做系統中的線程一一對應的,因此當一個線程在獲取鎖失敗後,會被切換到內核狀態而被掛起,當該線程獲取到鎖時又須要將其切換到內核狀態而喚醒該線程,而從用戶狀態切換到內核狀態的開銷是比較大的,在必定程度上會影響並不是性能,自旋鎖則是,當前線程在獲取鎖時,若是發現鎖已經被其餘線程佔有,它不是立刻阻塞本身,在不放棄CPU使用權的前提下,屢次嘗試獲取(默認是10此,可使用-XX:PreBlockSpinsh參數設置該值),頗有可能在後面的幾回嘗試中其餘線程已經釋放了鎖,若是嘗試指定的次數後仍沒有獲取到鎖則當前線程纔會被阻塞掛起,由此看來自旋鎖是使用CPU時間換取線程阻塞與調度的開銷,可是頗有可能這些CPU時間白白浪費了。

相關文章
相關標籤/搜索