java中的sychronized與鎖

sychronized與鎖的區別

在這裏插入圖片描述

sychronized

sychronized的作用是:用來確保多線程下的線程安全。

sychronized使用方法

  • 對象鎖:用sychronized修飾代碼塊 (手動去指定鎖對象)
  • 類鎖:用sychronized修飾普通方法(類鎖只能在同一時刻被一個對象擁有)

sychronized鎖的狀態:鎖升級的過程
無鎖狀態:鎖沒有線程進行爭奪。
偏向鎖狀態:第一個線程獲取該鎖,存儲該線程id,並改變對象的對象頭Mark Word中的狀態。
偏向鎖可以撤銷成無鎖狀態, 線程是不會主動釋放偏向鎖,需要等待其他線程來競爭
這種機制可以減小隻有一個線程下,CAS操作的性能消耗
輕量級鎖狀態:第二個線程嘗試獲取該鎖,當檢測到對象頭Mark Word已存在偏向鎖,進入自旋。
重量級鎖狀態:線程自旋失敗,進入阻塞狀態。

對象實例在堆中會被劃分三個組成部分:對象頭,實例數據,和對其填充。
對象頭中Mark Word中可以獲取該對象sychronized鎖的狀態。
sychronized鎖升級到重量級鎖時就是進入不可逆狀態。

sychronized在jdk1.6之後的優化

  • 自旋鎖
  • 自適應自旋鎖:根據自旋狀況,自動調整自旋的次數。
  • 鎖消除: JVM檢測到不可能存在共享數據競爭,這是JVM會對這些同步鎖進行鎖消除(無鎖狀態)
  • 鎖粗化: JVM檢測到同一個對象有連續的加鎖、解鎖操作,會合併成爲一個更大範圍的加鎖、解鎖操作,例如將加鎖解鎖操作移到for循環外面。
  • 輕量級鎖
  • 偏向鎖
  • 重量級鎖

樂觀鎖與悲觀鎖 (上鎖的的位置不同)

悲觀鎖:一段執行邏輯加上悲觀鎖,不同線程同時執行時,只能有一個線程執行,其他的線程在入口處等待,直到鎖被釋放. sychronized提供的是悲觀鎖

樂觀鎖:一段執行邏輯加上樂觀鎖,不同線程同時執行時,可以同時進入執行,在最後更新數據的時候要檢查這些數據是否被其他線程修改了(版本和執行初是否相同),沒有修改則進行更新,否則放棄本次操作.

如何實現樂觀鎖:
a.在數據庫 添加version字段
b.通過CAS(自旋鎖)操作:內存位置、期望原值、新值 (CAS稱爲無鎖棧、無鎖隊列 是一個原子性操作(一段內存同時只能有一個CPU方位))
update status=3 where id=111 and status=1;(增加篩選條件)

注:悲觀鎖 適合寫入頻繁場景,
樂觀鎖 適合讀取頻繁場景。
如何進一步 確保樂觀鎖事務原子性:
a.藉助C語言的ASC方式
b.加鎖:加入悲觀鎖

CAS操作是一種無鎖的方式來實現線程安全的方式,它有以下實現類:

  • AtomicInteger :用於整形
  • AtomicReference:用於引用類
  • AtomicStampedReference:引入時間戳用於對時間敏感的引用類 ##這個可以解決ABA問題
  • AtomicIntegerArray:用於數組
  • AtomicIntegerFieldUpdater:藉助反射機制,讓普通變量也能使用原則操作,如User類中的age;

CAS操作有那些問題:
1.ABA問題 (修改後又被修改回來)
2.每個線程都會嘗試去執行,對CPU的消耗比較大
3.只能保證一個共享變量的原子操作

讀寫鎖

讀寫鎖的規則是“ 讀讀不互斥,讀寫互斥,寫寫互斥”
可以理解爲: 讀鎖相當於一個共享鎖,寫鎖相當於互斥鎖。

讀寫鎖是基於AQS原理
AQS是將每一條請求共享資源的線程封裝成一個CLH鎖隊列的一個結點(Node),來實現鎖的分配。
AQS就是基於CLH隊列,用volatile修飾共享變量state,線程通過CAS去改變狀態符,成功則獲取鎖成功,失敗則進入等待隊列,等待被喚醒。

CLH(Craig,Landin,and Hagersten)隊列是一個虛擬的雙向隊列(FIFO先進先出),虛擬的雙向隊列即不存在隊列實例,僅存在節點之間的關聯關係。
在這裏插入圖片描述

公平鎖和非公平鎖

公平鎖和非公公平鎖的區別在於公平鎖會保證先來的線程會先獲取資源(其內部實現時AQS原理),而非公平不能保證。
sychronized是非公平鎖,ReenterLock(默認也是非公平鎖)中的讀寫鎖是公平鎖。

自旋鎖

自旋鎖,是指當一個線程在獲取鎖的時候,如果鎖已經被其它線程獲取,那麼該線程將循環等待,然後不斷的判斷鎖是否能夠被成功獲取,直到獲取到鎖纔會退出循環。

自旋鎖的狀態是通過自旋鎖的保持者來改變(命令模式).

互斥鎖

互斥鎖: 同一時刻只能有一個線程獲得互斥鎖,其餘線程處於睡眠狀態.

自旋鎖vs互斥鎖

  • 互斥鎖和自旋鎖都能夠保證了每次只有一個線程進行寫入操作,從而保證了多線程情況下數據的正確性。
  • 自旋鎖沒有上下文的切換性能比較高,但是存在死鎖的風險,只適用保持鎖時間比較短的情況下。
  • 互斥鎖會讓獲取不到資源的線程sleep(不放棄鎖,放棄對CPU的爭奪),進入線程阻塞狀態。

不放棄鎖:就意味着已經獲得該鎖的線程可以再次進入被該鎖鎖定的代碼塊。
這種鎖被稱爲可重入鎖, synchronized和ReenterLock都是可重入鎖。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章