何謂悲觀鎖與樂觀鎖

?

樂觀鎖對應于生活中樂觀的人總是想著事情往好的方向發展,悲觀鎖對應于生活中悲觀的人總是想著事情往壞的方向發展。這兩種人各有優缺點,不能不以場景而定說一種人好于另外一種人。

悲觀鎖

總是假設最壞的情況,每次去拿數據的時候都認為別人會修改,所以每次在拿數據的時候都會上鎖,這樣別人想拿這個數據就會阻塞直到它拿到鎖(共享資源每次只給一個線程使用,其它線程阻塞,用完后再把資源轉讓給其它線程)。傳統的關系型數據庫里邊就用到了很多這種鎖機制,比如行鎖,表鎖等,讀鎖,寫鎖等,都是在做操作之前先上鎖。Java中synchronizedReentrantLock等獨占鎖就是悲觀鎖思想的實現。

樂觀鎖

總是假設最好的情況,每次去拿數據的時候都認為別人不會修改,所以不會上鎖,但是在更新的時候會判斷一下在此期間別人有沒有去更新這個數據,可以使用版本號機制和CAS算法實現。樂觀鎖適用于多讀的應用類型,這樣可以提高吞吐量,像數據庫提供的類似于write_condition機制,其實都是提供的樂觀鎖。在Java中java.util.concurrent.atomic包下面的原子變量類就是使用了樂觀鎖的一種實現方式CAS實現的。

兩種鎖的使用場景

從上面對兩種鎖的介紹,我們知道兩種鎖各有優缺點,不可認為一種好于另一種,像樂觀鎖適用于寫比較少的情況下(多讀場景),即沖突真的很少發生的時候,這樣可以省去了鎖的開銷,加大了系統的整個吞吐量。但如果是多寫的情況,一般會經常產生沖突,這就會導致上層應用會不斷的進行retry,這樣反倒是降低了性能,所以一般多寫的場景下用悲觀鎖就比較合適。

樂觀鎖常見的兩種實現方式

樂觀鎖一般會使用版本號機制或CAS算法實現。

1. 版本號機制

一般是在數據表中加上一個數據版本號version字段,表示數據被修改的次數,當數據被修改時,version值會加一。當線程A要更新數據值時,在讀取數據的同時也會讀取version值,在提交更新時,若剛才讀取到的version值為當前數據庫中的version值相等時才更新,否則重試更新操作,直到更新成功。

舉一個簡單的例子: 假設數據庫中帳戶信息表中有一個 version 字段,當前值為 1 ;而當前帳戶余額字段( balance )為 $100 。

  1. 操作員 A 此時將其讀出( version=1 ),并從其帳戶余額中扣除 $50( $100-$50 )。
  2. 在操作員 A 操作的過程中,操作員B 也讀入此用戶信息( version=1 ),并從其帳戶余額中扣除 $20 ( $100-$20 )。
  3. 操作員 A 完成了修改工作,將數據版本號加一( version=2 ),連同帳戶扣除后余額( balance=$50 ),提交至數據庫更新,此時由于提交數據版本大于數據庫記錄當前版本,數據被更新,數據庫記錄 version 更新為 2 。
  4. 操作員 B 完成了操作,也將版本號加一( version=2 )試圖向數據庫提交數據( balance=$80 ),但此時比對數據庫記錄版本時發現,操作員 B 提交的數據版本號為 2 ,數據庫記錄當前版本也為 2 ,不滿足 “ 提交版本必須大于記錄當前版本才能執行更新 “ 的樂觀鎖策略,因此,操作員 B 的提交被駁回。

這樣,就避免了操作員 B 用基于 version=1 的舊數據修改的結果覆蓋操作員A 的操作結果的可能。

2. CAS算法

compare and swap(比較與交換),是一種有名的無鎖算法。無鎖編程,即不使用鎖的情況下實現多線程之間的變量同步,也就是在沒有線程被阻塞的情況下實現變量的同步,所以也叫非阻塞同步(Non-blocking Synchronization)。CAS算法涉及到三個操作數

  • 需要讀寫的內存值 V
  • 進行比較的值 A
  • 擬寫入的新值 B

當且僅當 V 的值等于 A時,CAS通過原子方式用新值B來更新V的值,否則不會執行任何操作(比較和替換是一個原子操作)。一般情況下是一個自旋操作,即不斷的重試

樂觀鎖的缺點

ABA 問題是樂觀鎖一個常見的問題

1 ABA 問題

如果一個變量V初次讀取的時候是A值,并且在準備賦值的時候檢查到它仍然是A值,那我們就能說明它的值沒有被其他線程修改過了嗎?很明顯是不能的,因為在這段時間它的值可能被改為其他值,然后又改回A,那CAS操作就會誤認為它從來沒有被修改過。這個問題被稱為CAS操作的 "ABA"問題。

JDK 1.5 以后的 AtomicStampedReference 類就提供了此種能力,其中的 compareAndSet 方法就是首先檢查當前引用是否等于預期引用,并且當前標志是否等于預期標志,如果全部相等,則以原子方式將該引用和該標志的值設置為給定的更新值。

2 循環時間長開銷大

自旋CAS(也就是不成功就一直循環執行直到成功)如果長時間不成功,會給CPU帶來非常大的執行開銷。 如果JVM能支持處理器提供的pause指令那么效率會有一定的提升,pause指令有兩個作用,第一它可以延遲流水線執行指令(de-pipeline),使CPU不會消耗過多的執行資源,延遲的時間取決于具體實現的版本,在一些處理器上延遲時間是零。第二它可以避免在退出循環的時候因內存順序沖突(memory order violation)而引起CPU流水線被清空(CPU pipeline flush),從而提高CPU的執行效率。

3 只能保證一個共享變量的原子操作

CAS 只對單個共享變量有效,當操作涉及跨多個共享變量時 CAS 無效。但是從 JDK 1.5開始,提供了AtomicReference類來保證引用對象之間的原子性,你可以把多個變量放在一個對象里來進行 CAS 操作.所以我們可以使用鎖或者利用AtomicReference類把多個共享變量合并成一個共享變量來操作。

CAS與synchronized的使用情景

簡單的來說CAS適用于寫比較少的情況下(多讀場景,沖突一般較少),synchronized適用于寫比較多的情況下(多寫場景,沖突一般較多)

  1. 對于資源競爭較少(線程沖突較輕)的情況,使用synchronized同步鎖進行線程阻塞和喚醒切換以及用戶態內核態間的切換操作額外浪費消耗cpu資源;而CAS基于硬件實現,不需要進入內核,不需要切換線程,操作自旋幾率較少,因此可以獲得更高的性能。
  2. 對于資源競爭嚴重(線程沖突嚴重)的情況,CAS自旋的概率會比較大,從而浪費更多的CPU資源,效率低于synchronized。

補充: Java并發編程這個領域中synchronized關鍵字一直都是元老級的角色,很久之前很多人都會稱它為 “重量級鎖” 。但是,在JavaSE 1.6之后進行了主要包括為了減少獲得鎖和釋放鎖帶來的性能消耗而引入的 偏向鎖輕量級鎖 以及其它各種優化之后變得在某些情況下并不是那么重了。synchronized的底層實現主要依靠 Lock-Free 的隊列,基本思路是 自旋后阻塞競爭切換后繼續競爭鎖稍微犧牲了公平性,但獲得了高吞吐量。在線程沖突較少的情況下,可以獲得和CAS類似的性能;而線程沖突嚴重的情況下,性能遠高于CAS。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/272409.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/272409.shtml
英文地址,請注明出處:http://en.pswp.cn/news/272409.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

寒哥細談之AutoLayout全解

看到群中好多朋友還停留在Frame布局的痛苦時代,以及有些開發者接手別人的就項目發現布局一團亂。而且沒有啟動圖的時候并不是真正真正適配iPhone 6(S)、iPhone6(S) Plus等設備 。寒哥準備盡可能詳細的講一講我所掌握的AutoLayout 。AutoLayout很難?我覺得…

最難學的5種編程語言排行

每個程序員都熟悉許多編程語言。許多編程語言都是高級的,它們的語法是人類可讀的。然而,也有一些低級語言,對于一個人來說,讀起來很困難,但是可以理解。然而,您是否遇到過一種既不可讀又不可理解的編程語言…

[小北De編程手記] : Lesson 02 - Selenium For C# 之 核心對象

從這一篇開始,開始正式的介紹Selenium 以及相關的組件,本文的將討論如下問題: Selenium基本的概念以及在企業化測試框架中的位置Selenium核心對象(瀏覽器驅動) Web DriverSelenium核心對象(Dom元素&#xf…

Java中HashMap的entrySet()你會用了嗎

Map中存放的元素均為鍵值對&#xff0c;故每一個鍵值對必然存在一個映射關系。 Map中采用Entry內部類來表示一個映射項&#xff0c;映射項包含Key和Value Map.Entry里面包含getKey()和getValue()方法 Set<Entry<T,V>> entrySet() 該方法返回值就是這個map中各個鍵…

JS獲取請求URL相關參數

今天在找獲取當前網址除去參數的js方式&#xff0c;結果自己會的竟然只有window.location.href 先看一個示例 用javascript獲取url網址信息 <script type"text/javascript"> document.write("location.host"location.host"<br>"); d…

wiki語法大全

Wiki語法大全 編輯一個維客頁面十分容易。只要點擊頁面上方的“編輯本頁”或右側的[編輯]鏈接即可修改該頁&#xff0c;或點擊“討論本頁”然后再點擊“編輯頁面”來討論該頁面。點擊后您就會看到一個包含那個Wiki頁面的可編輯的文字區域。 先將文字復制到您最喜歡的文字編輯器…

驅動認知

用戶態 App&#xff1a;cp指令&#xff0c;ftp的項目等等這就是App的開發。 App開發需要C的基礎和C庫&#xff0c;C庫講到文件&#xff0c;進程&#xff0c;進程間通信&#xff0c;線程&#xff0c;網絡&#xff0c;界面&#xff08;GTk&#xff09;。 C庫&#xff08;是linux標…

TreeMap實現排序

TreeMap TreeMap實現SortMap接口&#xff0c;能夠把它保存的記錄根據鍵排序&#xff0c;默認是按鍵值的升序排序&#xff0c;也可以指定排序的比較器。當用Iterator遍歷TreeMap時&#xff0c;得到的記錄是排過序的。 TreeMap取出來的是排序后的鍵值對。但如果您要按自然順序或…

圖解分布式架構的演進過程!

一、什么是分布式架構分布式系統&#xff08;distributed system&#xff09; 是建立在網絡之上的軟件系統。內聚性&#xff1a;是指每一個數據庫分布節點高度自治&#xff0c;有本地的數據庫管理系統。透明性&#xff1a;是指每一個數據庫分布節點對用戶的應用來說都是透明的&…

CSS 布局實例系列(四)如何實現容器中每一行的子容器數量隨著瀏覽器寬度的變化而變化?...

Hello&#xff0c;小朋友們&#xff0c;還記得我是誰嗎&#xff1f;對了&#xff0c;我就是~超威~好啦&#xff0c;言歸正傳&#xff0c;今天的布局實例是&#xff1a; 實現一個浮動布局&#xff0c;紅色容器中每一行的藍色容器數量隨著瀏覽器寬度的變化而變化&#xff0c;就如…

基于框架編寫驅動代碼

操作驅動的上層代碼&#xff08;pin4test&#xff09; #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h>void main() {int fd,data;fd open("/dev/pin4",O_RDWR);if(fd<0){printf("open fail…

nacos在windows下安裝

1:訪問https://github.com/alibaba/nacos/releases下載nacos 2:下載到本地解壓 3:點擊startup.cmd 啟動nacos 4:訪問 http://127.0.0.1:8848/nacos 輸入賬號和密碼&#xff0c;nacos/nacos

關于SQL優化這些你了解嗎?

背景在當今這個互聯網的時代無非要解決兩大難題&#xff0c;其一是信息安全&#xff0c;其二就是數據的存儲。而信息安全則是在數據存儲的基礎之上。一個公司從剛開始成立到發展成一個有上百人甚至上千人團隊的時候&#xff0c;公司的業務量是呈上升趨勢&#xff0c;客戶及用戶…

圖片安全保護(未啟動)

圖片安全保護&#xff08;未啟動&#xff09; 圖片安全保護&#xff08;未啟動&#xff09;posted on 2016-01-27 22:54 代碼35 閱讀(...) 評論(...) 編輯 收藏 轉載于:https://www.cnblogs.com/xinglizhenchu/p/5164847.html

微機總線地址,物理地址 ,虛擬地址

總線地址 地址總線 (Address Bus&#xff1b;又稱&#xff1a;位址總線) 屬于一種電腦總線 &#xff08;一部份&#xff09;&#xff0c;是由CPU 或有DMA 能力的單元&#xff0c;用來溝通這些單元想要存取&#xff08;讀取/寫入&#xff09;電腦內存元件/地方的實體位址。 自己…

Navicat遠程連接linux下mysql服務器1045錯誤解決辦法在這兒

1&#xff1a;首先通過xshell工具或者你熟悉的工具連接遠程linux下的服務器 mysql -uroot -p 然后輸入密碼 2.進行授權 如果想root用戶使用password從任何主機連接到mysql服務器的話。 GRANT ALL PRIVILEGES ON *.* TO root% IDENTIFIED BY 你的mysql密碼 WITH GRANT O…

內存劃分

一、內存分配 一個由C/C編譯的程序占用的內存分為以下幾個部分 1、棧區&#xff08;stack&#xff09;&#xff1a; 由編譯器自動分配釋放 &#xff0c;存放函數的參數值&#xff0c;局部變量的值等。其操作方式類似于數據結構中的棧。 2、堆區&#xff08;heap&a…

樹莓派 博通BCM2835芯片手冊

手冊提取鏈接 鏈接&#xff1a;https://pan.baidu.com/s/1fdmIBNn1Pr1j3-ercNhKJg 提取碼&#xff1a;8y1b 驅動的兩大利器&#xff1a; 1、電路圖:通過電路圖找到寄存器 2、芯片手冊 樹莓派有54個通用IO口(0到53)&#xff0c;所有GPIO口至少有兩個可選功能&#xff08;輸入輸…

MVC實現實現文件流打包成壓縮包

MVC實現實現文件流打包成壓縮包1、使用壓縮類庫SharpZipLib SharpZipLib 是一款比較經典實用C#壓縮類庫SharpZipLib 庫特點&#xff1a;功能豐富、穩定 &#xff0c;支持主流 zip、Gzip Tar BZip2 格式2、項目中引用SharpZipLib的官方地址是&#xff1a;http://icsharpcode.git…

Intellij idea 報錯:Error : java 不支持發行版本5

idea運行出現了這個錯誤 解決辦法按下圖所示&#xff1a;