樂觀鎖和悲觀鎖
悲觀鎖:具有強烈的獨佔和排他特性。它指的是對資料被外界(包括本系統當前的其他事務,以及來自外部系統的事務處理)修改持保守態度。因此,在整個資料處理過程中,將資料處於鎖定狀態。悲觀鎖的實現,往往依靠資料庫提供的鎖機制(也只有資料庫層提供的鎖機制才能真正保證資料訪問的排他性,否則,即使在本系統中實現了加鎖機制,也無法保證外部系統不會修改資料)。
之所以叫做悲觀鎖,是因為這是一種對資料的修改持有悲觀態度的併發控制方式。總是假設最壞的情況,每次讀取資料的時候都預設其他執行緒會更改資料,因此需要進行加鎖操作,當其他執行緒想要訪問資料時,都需要阻塞掛起。悲觀鎖的實現:
1、傳統的關係型資料庫使用這種鎖機制,比如行鎖、表鎖、讀鎖、寫鎖等,都是在操作之前先上鎖。
2、Java 裡面的同步 synchronized 關鍵字的實現。
悲觀鎖主要分為共享鎖和排他鎖:
共享鎖【shared locks】又稱為讀鎖,簡稱 S 鎖。顧名思義,共享鎖就是多個事務對於同一資料可以共享一把鎖,都能訪問到資料,但是隻能讀不能修改。
排他鎖【exclusive locks】又稱為寫鎖,簡稱 X 鎖。顧名思義,排他鎖就是不能與其他鎖並存,如果一個事務獲取了一個數據行的排他鎖,其他事務就不能再獲取該行的其他鎖,包括共享鎖和排他鎖。獲取排他鎖的事務可以對資料行讀取和修改。
樂觀鎖:樂觀鎖是相對悲觀鎖而言的,樂觀鎖假設資料一般情況不會造成衝突,所以在資料進行提交更新的時候,才會正式對資料的衝突與否進行檢測,如果衝突,則返回給使用者異常資訊,讓使用者決定如何去做。樂觀鎖適用於讀多寫少的場景,這樣可以提高程式的吞吐量。樂觀鎖採取了更加寬鬆的加鎖機制。也是為了避免資料庫幻讀、業務處理時間過長等原因引起資料處理錯誤的一種機制,但樂觀鎖不會刻意使用資料庫本身的鎖機制,而是依據資料本身來保證資料的正確性。樂觀鎖的實現:
1、CAS 實現:Java 中java.util.concurrent.atomic包下面的原子變數使用了樂觀鎖的一種 CAS 實現方式。
2、版本號控制:一般是在資料表中加上一個資料版本號 version 欄位,表示資料被修改的次數。當資料被修改時,version 值會 +1。當執行緒 A 要更新資料時,在讀取資料的同時也會讀取 version 值,在提交更新時,若剛才讀取到的 version 值與當前資料庫中的 version 值相等時才更新,否則重試更新操作,直到更新成功。
3、樂觀併發控制相信事務之間的資料競爭(data race)的概率是比較小的,因此儘可能直接做下去,直到提交的時候才去鎖定,所以不會產生任何鎖和死鎖。