Java并發篇_樂觀鎖與悲觀鎖

樂觀鎖對應于生活中樂觀的人總是想著事情往好的方向發展,悲觀鎖對應于生活中悲觀的人總是想著事情往壞的方向發展。

一、引入概念

1、悲觀鎖

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

2、樂觀鎖

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

3、兩種鎖的使用場景

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

二、樂觀鎖的實現方式——CAS算法

1、CAS算法

什么是CAS?

CAS全稱為Compare And Swap即比較并交換,其算法公式如下:

函數公式:CAS(V,E,N)V:表示要更新的變量E:表示預期值N:表示新值

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-jpYccXsQ-1600049416983)(https://pics3.baidu.com/feed/810a19d8bc3eb135116a411f9e3739d6fc1f4497.jpeg?token=c57722e5bdc2b5d2e02a24affb2e2ac9&s=6AAC3C6203AEC5EF5CF530CE000080B1)]CAS

如果V值等于E值,則將V的值設為N。若V值和E值不同,則說明已經有其他線程做了更新,則當前線程什么都不做。通俗的理解就是CAS操作需要我們提供一個期望值,當期望值與當前線程的變量值相同時,說明還沒線程修改該值,當前線程可以進行修改,也就是執行CAS操作,但如果期望值與當前線程不符,則說明該值已被其他線程修改,此時不執行更新操作,但可以選擇重新讀取該變量再嘗試再次修改該變量,也可以放棄操作。

2、“ABA問題”與“版本號機制”

CAS 會導致“ABA 問題”。CAS 算法實現一個重要前提需要取出內存中某時刻的數據,而在下時刻比較并替換,那么在這個時間差類會導致數據的變化。

比如說一個線程 one 從內存位置 V 中取出 A,這時候另一個線程 two 也從內存中取出 A,并且two 進行了一些操作變成了 B,然后 two 又將 V 位置的數據變成 A,這時候線程 one 進行 CAS 操作發現內存中仍然是 A,然后 one 操作成功。盡管線程 one 的 CAS 操作成功,但是不代表這個過程就是沒有問題的。

部分樂觀鎖的實現是通過版本號(version)的方式來解決 ABA 問題,樂觀鎖每次在執行數據的修改操作時,都會帶上一個版本號,一旦版本號和數據的版本號一致就可以執行修改操作并對版本號執行+1 操作,否則就執行失敗。因為每次操作的版本號都會隨之增加,所以不會出現 ABA 問題,因為版本號只會增加不會減少。

3、Java中的CAS

JDK5增加java.util.concurrent包,其中很多類使用了CAS操作。這些CAS操作基于Unsafe類中的native方法實現:

//第一個參數o為給定對象,offset為對象內存的偏移量,通過這個偏移量迅速定位字段并設置或獲取該字段的值,
//expected表示期望值,x表示要設置的值,下面3個方法都通過CAS原子指令執行操作,
//設置成功返回true,否則返回false。
public final native boolean compareAndSwapObject(Object o, long offset,Object expected, Object x);
public final native boolean compareAndSwapInt(Object o, long offset,int expected,int x);
public final native boolean compareAndSwapLong(Object o, long offset,long expected,long x);

由于CAS作用的對象在主存里而不是在線程的高速緩存里,CAS操作在Java中需要配合volatile使用。

Java中的CAS主要包含以下幾個問題:

  • ABA問題,即變量V初次讀時是A值,被賦值時也是A值,但期間變量被賦值成B值,CAS會誤認為他從沒被修改過。AtomicStampedReference和AtomicMarckableReference類提供了監測ABA問題的能力,其中的compareAndSet方法首先檢查當前引用是否等于預期引用,并且當前標志等于預期標志,全部相等則以原子方式將該引用和該標志的值設置為給定的更新值。
  • 循環開銷,自旋CAS長時間不成功會給CPU帶來非常大的執行開銷。若JVM能支持pause命令,效率有一定提升。因為pause命令一方面可以延遲流水線執行命令,使CPU不會消耗過多的執行資源,另一方面可以避免退出循環時由內存順序沖突引起的CPU流水線被沖突,從而提高CPU的執行效率。
  • 只能保證一個共享變量的原子操作,當操作涉及跨多個共享變量時CAS無效。可用AtomicReference封裝多個字段來保證引用對象之間的原子性。

三、悲觀鎖——synchronized

1、synchronized

synchronized是Java中的關鍵字,是一種同步鎖。可修飾實例方法,靜態方法,代碼塊。

  • 修飾實例方法:對當前實例加鎖,進入同步代碼前要獲得當前實例的鎖
  • 修飾靜態方法:對當前類對象加鎖,進入同步代碼前要獲得當前類對象的鎖
  • 修飾代碼塊:指定加鎖對象,對給定對象加鎖,進入同步代碼庫前要獲得給定對象的鎖。

2、CAS與synchronized的使用情景

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

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

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

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

相關文章

Redhat與ubuntu配置網卡

redhat linux中設置網卡固定ip之前在xwindow下的redhat-config-network 設置網卡固定ip發現不起作用,設置好后就是ping不通。就查了些資料,更改 /etc/sysconfig/network-scripts/ifcfg-eth0(第一個網卡為eth0),配置dns的文件為 /etc/resolv.c…

SSH軟件包:Sftp,scp和ssh-agent

這篇文章的中心是介紹在ssh軟件包中非常有用的程序如:sftp,scp,ssh-agent,和ssh-add。在下文中我們假設sshd2守護進程很好地被設置并且運行良好。Sftp和scp總覽讓我們把注意力集中到sftp和scp上。第一個(sftp安全文件傳…

JAVA并發篇_公平鎖與非公平鎖

簡單的來說,如果一個線程組里,能保證每個線程都能拿到鎖,那么這個鎖就是公平鎖。相反,如果保證不了每個線程都能拿到鎖,也就是存在有線程餓死,那么這個鎖就是非公平鎖。 一、引入概念 1、公平鎖&#xff1…

Java并發篇_進程線程

一個進程包括由操作系統分配的內存空間,包含一個或多個線程。一個線程不能獨立的存在,它必須是進程的一部分。一個進程一直運行,直到所有的非守護線程都結束運行后才能結束。 多線程能滿足程序員編寫高效率的程序來達到充分利用 CPU 的目的。…

Real提示“作為受限用戶,您無足夠的windows操作權限”的解決辦法

運行Regedit.exe,翻到HKEY_CLASSES_ROOT/Software,刪除Software;然后關閉注冊表,再運行Regedit.exe,翻到HKEY_CLASSES_ROOT/Software,點右鍵選擇“權限”,各個組都設置為“完全控制”和“讀取”…

Java并發篇_線程詳解

線程(thread) 是操作系統能夠進行運算調度的最小單位。它被包含在進程之中,是進程中的實際運作單位。一條線程指的是進程中一個單一順序的控制流,一個進程中可以并發多個線程,每條線程并行執行不同的任務。 一、線程的…

修改MYSQL最大連接數的3種方法

MYSQL數據庫安裝完成后,默認最大連接數是100,一般流量稍微大一點的論壇或網站這個連接數是遠遠不夠的,增加默認MYSQL連接數的方法有兩個 方法一:進入MYSQL安裝目錄 打開MYSQL配置文件 my.ini 或 my.cnf查找 max_connections100 …

可擴展的編程語言——Scala

一、Scala是什么 Scala是一種多范式的編程語言,其設計的初衷是要集成面向對象編程和函數式編程的各種特性。Scala運行于Java平臺(Java虛擬機),并兼容現有的Java程序。 ? Scala語言的名稱來自于"可伸展的語言"。之所以…

ubuntu7.10 apache+php+mysql配置

本篇文章 經過許多次的測試和修改已經完成了在Ubuntu7.10 下 安裝配置 ApachePHPMySQL的所有的工作. 1、在Ubuntu7.10 下安裝 Apache2PHP5MySQL sudo apt-get install apache2 libapache2-mod-php5 php5 php5-gd mysql-server php5-mysql phpmyadmin在下載來自動安裝配置的時候…

Spark-大規模數據處理計算引擎

官網:http://spark.apache.org 一、Spark是什么 Spark是一種快速、通用、可擴展的大數據分析引擎,2009年誕生于加州大學伯克利分校AMPLab,2010年開源,2013年6月成為Apache孵化項目,2014年2月成為Apache頂級項目。項目是…

MySQL Replace INTO的使用

REPLACE的運行與INSERT很相像。只有一點除外,如果表中的一個舊記錄與一個用于PRIMARY KEY或一個UNIQUE索引的新記錄具有相同的值,則在新記錄被插入之前,舊記錄被刪除。請參見13.2.4節,“INSERT語法”。 注意,除非表有…

CentOS7下Spark集群的安裝

從物理部署層面上來看,Spark主要分為兩種類型的節點,Master節點和Worker節點,Master節點主要運行集群管理器的中心化部分,所承載的作用是分配Application到Worker節點,維護Worker節點,Driver,Ap…

Scala中class與object區別

calss scala編譯器會字段幫我們生產一個私有字段和2個公有方法get和set scala 中沒有 static 關鍵字,所以 對于一個class來說,所有的方法和成員變量在實例被 new 出來之前都是無法訪問的 因此在class中的main方法沒什么用了 scala 的object 中所有成員…

如何編寫一個shell腳本

本文結合大量實例闡述如何編寫一個shell腳本。 為什么要進行shell編程 在Linux系統中,雖然有各種各樣的圖形化接口工具,但是sell仍然是一個非常靈活的工具。Shell不僅僅是命令的收集,而且是一門非常棒的編程語言。您可以通過使用shell使大量的…

Scala變量和常用數據類型

一、 聲明值和變量 Scala聲明變量有兩種方式,一個用val,一個用var。 聲明方式:val / var 變量名 : 變量類型 變量值 val定義的值是不可變的,它不是一個常量,是不可變量,或稱之為只讀變量。 val示例&am…

ubuntu7.10下的vi用的怪怪的

到網上查了一下,原來是ubuntu7.10默認安裝的是vim-tiny.可以重新安裝vim-full #dpkg -l 如果是vim-tiny #apt-get install vim-full

(轉)JVM監控工具介紹

2008年03月04日 16:57原作者: stone2083 原文地址:http://www.blogjava.net/stone2083/archive/2008/02/25/182081.htmljstatd啟動jvm監控服務。它是一個基于rmi的應用,向遠程機器提供本機jvm應用程序的信息。默認端口1099。實例:…

Scala的控制結構

一、 if else表達式 scala中沒有三目運算符,因為根本不需要。scala中if else表達式是有返回值的,如果if或者else返回的類型不一樣,就返回Any類型(所有類型的公共超類型)。 例如:if else返回類型一樣 scal…

【Kubernetes】控制器Statefulset

Statefulset控制器 一、概念二、Statefulset資源清單文件編寫技巧2.1、查看定義Statefulset資源需要的字段2.2、查看statefulset.spec字段如何定義2.3、查看statefulset的spec.template字段如何定義 三、Statefulset使用案例:部署web站點3.1、編寫一個Statefulset資…

Scala 函數

scala定義函數的標準格式為: def 函數名(參數名1: 參數類型1, 參數名2: 參數類型2) : 返回類型 {函數體} 函數示例1:返回Unit類型的函數 def shout1(content: String) : Unit {println(content) }函數示例2:返回Unit類型的函數&#xff0…