悲觀鎖
總是假設最壞的情況,每次去拿數(shù)據(jù)的時候都認為別人會修改废亭,所以每次在拿數(shù)據(jù)的時候都會上鎖宿崭,這樣別人想拿這個數(shù)據(jù)就會阻塞直到它拿到鎖(共享資源每次只給一個線程使用愿阐,其它線程阻塞,用完后再把資源轉讓給其它線程)回挽。傳統(tǒng)的關系型數(shù)據(jù)庫里邊就用到了很多這種鎖機制没咙,比如行鎖,表鎖等千劈,讀鎖祭刚,寫鎖等,都是在做操作之前先上鎖墙牌。Java中synchronized和ReentrantLock等獨占鎖就是悲觀鎖思想的實現(xiàn)涡驮。
樂觀鎖
總是假設最好的情況,每次去拿數(shù)據(jù)的時候都認為別人不會修改喜滨,所以不會上鎖捉捅,但是在更新的時候會判斷一下在此期間別人有沒有去更新這個數(shù)據(jù),可以使用版本號機制和CAS算法實現(xiàn)虽风。樂觀鎖適用于多讀的應用類型棒口,這樣可以提高吞吐量,像數(shù)據(jù)庫提供的類似于write_condition機制辜膝,其實都是提供的樂觀鎖无牵。在Java中java.util.concurrent.atomic包下面的原子變量類就是使用了樂觀鎖的一種實現(xiàn)方式CAS實現(xiàn)的。
兩種鎖的使用場景
從上面對兩種鎖的介紹厂抖,我們知道兩種鎖各有優(yōu)缺點茎毁,不可認為一種好于另一種,像樂觀鎖適用于寫比較少的情況下(多讀場景)忱辅,即沖突真的很少發(fā)生的時候充岛,這樣可以省去了鎖的開銷保檐,加大了系統(tǒng)的整個吞吐量。但如果是多寫的情況崔梗,一般會經(jīng)常產(chǎn)生沖突夜只,這就會導致上層應用會不斷的進行retry,這樣反倒是降低了性能蒜魄,所以一般多寫的場景下用悲觀鎖就比較合適扔亥。
樂觀鎖實現(xiàn)
-
版本號機制
- 一般是在數(shù)據(jù)表中加上一個數(shù)據(jù)版本號version字段,表示數(shù)據(jù)被修改的次數(shù)谈为,當數(shù)據(jù)被修改時旅挤,version值會加一。當線程A要更新數(shù)據(jù)值時伞鲫,在讀取數(shù)據(jù)的同時也會讀取version值粘茄,在提交更新時,若剛才讀取到的version值為當前數(shù)據(jù)庫中的version值相等時才更新秕脓,否則重試更新操作柒瓣,直到更新成功。
- CAS算法
- 需要讀寫的內(nèi)存值 V
- 進行比較的值 A
- 擬寫入的新值 B
- 當且僅當 V 的值等于 A時吠架,CAS通過原子方式用新值B來更新V的值芙贫,否則不會執(zhí)行任何操作(比較和替換是一個原子操作)。一般情況下是一個自旋操作傍药,即不斷的重試磺平。