1. 程式人生 > >鎖(悲觀鎖和樂觀鎖)

鎖(悲觀鎖和樂觀鎖)

悲觀鎖:假定會發生併發衝突,遮蔽一切可能違反資料完整性的操作。
樂觀鎖:假設不會發生併發衝突,只在提交操作時檢查是否違反資料完整性。樂觀鎖不能解決髒讀的問題。

悲觀鎖具有很強的獨佔性,也是最安全的.而樂觀鎖很開放,效率高,安全性比悲觀鎖低。

Java樂觀鎖悲觀鎖

cpu是時分複用的,也就是把cpu的時間片,分配給不同的thread/process輪流執行,時間片與時間片之間,需要進行cpu切換,也就是會發生程序的切換。切換涉及到清空暫存器,快取資料。然後重新載入新的thread所需資料。當一個執行緒被掛起時,加入到阻塞佇列,在一定的時間或條件下,在通過notify(),notifyAll()喚醒回來。在某個資源不可用的時候,就將cpu讓出,把當前等待執行緒切換為阻塞狀態。等到資源(比如一個共享資料)可用了,那麼就將執行緒喚醒,讓他進入runnable狀態等待cpu排程。這就是典型的悲觀鎖

的實現。
獨佔鎖是一種悲觀鎖,synchronized就是一種獨佔鎖,它假設最壞的情況,並且只有在確保其它執行緒不會造成干擾的情況下執行,會導致其它所有需要鎖的執行緒掛起,等待持有鎖的執行緒釋放鎖。但是,由於在程序掛起和恢復執行過程中存在著很大的開銷。當一個執行緒正在等待鎖時,它不能做任何事,所以悲觀鎖有很大的缺點。

舉個例子,如果一個執行緒需要某個資源,但是這個資源的佔用時間很短,當執行緒第一次搶佔這個資源時,可能這個資源被佔用,如果此時掛起這個執行緒,可能立刻就發現資源可用,然後又需要花費很長的時間重新搶佔鎖,時間代價就會非常的高。

所以就有了樂觀鎖的概念,核心思路就是,每次不加鎖而是假設沒有衝突而去完成某項操作,如果因為衝突失敗就重試,直到成功為止。在上面的例子中,某個執行緒可以不讓出cpu,而是一直while迴圈,如果失敗就重試,直到成功為止。所以,當資料爭用不嚴重時,樂觀鎖效果更好。比如CAS就是一種樂觀鎖思想的應用。
JDK1.5中引入了底層的支援,在int、long和物件的引用等型別上都公開了CAS的操作,並且JVM把它們編譯為底層硬體提供的最有效的方法,在執行CAS的平臺上,執行時把它們編譯為相應的機器指令。在java.util.concurrent.atomic

包下面的所有的原子變數型別中,比如AtomicInteger,都使用了這些底層的JVM支援為數字型別的引用型別提供一種高效的CAS操作。

在CAS操作中,會出現ABA問題。就是如果V的值先由A變成B,再由B變成A,那麼仍然認為是發生了變化,並需要重新執行演算法中的步驟。有簡單的解決方案:不是更新某個引用的值,而是更新兩個值,包括一個引用和一個版本號,即使這個值由A變為B,然後為變為A,版本號也是不同的。

AtomicStampedReferenceAtomicMarkableReference支援在兩個變數上執行原子的條件更新。AtomicStampedReference更新一個“物件-引用”二元組,通過在引用上加上“版本號”,從而避免ABA問題,AtomicMarkableReference將更新一個“物件引用-布林值”的二元組。

資料庫樂觀鎖悲觀鎖

悲觀鎖

資料庫管理系統(DBMS)中的併發控制(樂觀鎖悲觀鎖)的任務是確保在多個事務同時存取資料庫中同一資料時不破壞事務的隔離性和統一性以及資料庫的統一性。

在關係資料庫管理系統裡,悲觀併發控制(又名“悲觀鎖”,Pessimistic Concurrency
Control,縮寫“PCC”)是一種併發控制的方法。它可以阻止一個事務以影響其他使用者的方式來修改資料。如果一個事務執行的操作都某行資料應用了鎖,那只有當這個事務把鎖釋放,其他事務才能夠執行與該鎖衝突的操作。

悲觀併發控制主要用於資料爭用激烈的環境,以及發生併發衝突時使用鎖保護資料的成本要低於回滾事務的成本的環境中。
悲觀鎖,它指的是對資料被外界(包括本系統當前的其他事務,以及來自外部系統的事務處理)修改持保守態度(悲觀),因此,在整個資料處理過程中,將資料處於鎖定狀態。 悲觀鎖的實現,往往依靠資料庫提供的鎖機制 (也只有資料庫層提供的鎖機制才能真正保證資料訪問的排他性,否則,即使在本系統中實現了加鎖機制,也無法保證外部系統不會修改資料)

在資料庫中,悲觀鎖的流程如下:

在對任意記錄進行修改前,先嚐試為該記錄加上排他鎖(exclusive locking)。
如果加鎖失敗,說明該記錄正在被修改,那麼當前查詢可能要等待或者丟擲異常。 具體響應方式由開發者根據實際需要決定。
如果成功加鎖,那麼就可以對記錄做修改,事務完成後就會解鎖了。 其間如果有其他對該記錄做修改或加排他鎖的操作,都會等待我們解鎖或直接丟擲異常。

優點與不足

悲觀併發控制實際上是“先取鎖再訪問”的保守策略,為資料處理的安全提供了保證。但是在效率方面,處理加鎖的機制會讓資料庫產生額外的開銷,還有增加產生死鎖的機會;另外,在只讀型事務處理中由於不會產生衝突,也沒必要使用鎖,這樣做只能增加系統負載;還有會降低了並行性,一個事務如果鎖定了某行資料,其他事務就必須等待該事務處理完才可以處理那行數

樂觀鎖

在關係資料庫管理系統裡,樂觀併發控制(又名“樂觀鎖”,Optimistic Concurrency
Control,縮寫“OCC”)是一種併發控制的方法。它假設多使用者併發的事務在處理時不會彼此互相影響,各事務能夠在不產生鎖的情況下處理各自影響的那部分資料。在提交資料更新之前,每個事務會先檢查在該事務讀取資料後,有沒有其他事務又修改了該資料。如果其他事務有更新的話,正在提交的事務會進行回滾。樂觀事務控制最早是由孔祥重(H.T.Kung)教授提出。

樂觀鎖( Optimistic Locking ) 相對悲觀鎖而言,樂觀鎖假設認為資料一般情況下不會造成衝突,所以在資料進行提交更新的時候,才會正式對資料的衝突與否進行檢測,如果發現衝突了,則讓返回使用者錯誤的資訊,讓使用者決定如何去做。
相對於悲觀鎖,在對資料庫進行處理的時候,樂觀鎖並不會使用資料庫提供的鎖機制。一般的實現樂觀鎖的方式就是記錄資料版本。
資料版本,為資料增加的一個版本標識。當讀取資料時,將版本標識的值一同讀出,資料每更新一次,同時對版本標識進行更新。當我們提交更新的時候,判斷資料庫表對應記錄的當前版本資訊與第一次取出來的版本標識進行比對,如果資料庫表當前版本號與第一次取出來的版本標識值相等,則予以更新,否則認為是過期資料。
實現資料版本有兩種方式,第一種是使用版本號,第二種是使用時間戳。
使用版本號

使用資料版本(Version)記錄機制實現,這是樂觀鎖最常用的一種實現方式。何謂資料版本?即為資料增加一個版本標識,一般是通過為資料庫表增加一個數字型別的
“version”
欄位來實現。當讀取資料時,將version欄位的值一同讀出,資料每更新一次,對此version值加一。當我們提交更新的時候,判斷資料庫表對應記錄的當前版本資訊與第一次取出來的version值進行比對,如果資料庫表當前版本號與第一次取出來的version值相等,則予以更新,否則認為是過期資料。

使用時間戳

樂觀鎖定的第二種實現方式和第一種差不多,同樣是在需要樂觀鎖控制的table中增加一個欄位,名稱無所謂,欄位型別使用時間戳(timestamp),
和上面的version類似,也是在更新提交的時候檢查當前資料庫中資料的時間戳和自己更新前取到的時間戳進行對比,如果一致則OK,否則就是版本衝突。

優點與不足

樂觀併發控制相信事務之間的資料競爭(data
race)的概率是比較小的,因此儘可能直接做下去,直到提交的時候才去鎖定,所以不會產生任何鎖和死鎖。但如果直接簡單這麼做,還是有可能會遇到不可預期的結果,例如兩個事務都讀取了資料庫的某一行,經過修改以後寫回資料庫,這時就遇到了問題。