摘要:它倆是不沖突的,也就是說獲取了類鎖的線程和獲取了對象鎖的線程是不沖突的可重入鎖住了鎖住了當線程進入到的方法時,此時拿到了實例對象的鎖。當一個線程執行的代碼出現異常時,其所持有的鎖會自動釋放。
Java鎖機制 synchronized鎖
synchronized 簡介
synchronized是Java的一個關鍵字,它能夠將代碼塊(方法)鎖起來
synchronized是一種互斥鎖
一次只能允許一個線程進入被鎖住的代碼塊
synchronized是一種內置鎖/監視器鎖
Java中每個對象都有一個內置鎖(監視器,也可以理解成鎖標記),而synchronized就是使用對象的內置鎖(監視器)來將代碼塊(方法)鎖定的!
synchronized保證了線程的原子性。
被保護的代碼塊是一次被執行的,沒有任何線程會同時訪問
synchronized還保證了可見性。
當執行完synchronized之后,修改后的變量對其他的線程是可見的
synchronized底層原理
同步代碼塊:
monitorenter和monitorexit指令實現的
同步方法(在這看不出來需要看JVM底層實現)
方法修飾符上的ACC_SYNCHRONIZED實現。
Java中的synchronized,通過使用內置鎖,來實現對變量的同步操作,進而實現了對變量操作的原子性和其他線程對變量的可見性,從而確保了并發情況下的線程安全。
synchronized一般我們用來修飾三種東西
修飾普通方法:用的是 該對象(內置鎖)
// 修飾普通方法,此時用的鎖是 該對象(內置鎖) public synchronized void test() { }
修飾代碼塊:用的是 該對象(內置鎖)
// 修飾代碼塊,此時用的鎖是 該對象(內置鎖)--->this synchronized (this){ }當然了,我們使用synchronized修飾代碼塊時未必使用this,還可以使用其他的對象(隨便一個對象都有一個內置鎖)
稱之為-->客戶端鎖,這是不建議使用的
修飾靜態方法:用的是 類鎖(類的字節碼文件對象):XX.class
// 修飾靜態方法,此時用的鎖是 類鎖(類的字節碼文件對象): XX.class public static synchronized void test() { }
synchronized修飾靜態方法獲取的是類鎖(類的字節碼文件對象),synchronized修飾普通方法或代碼塊獲取的是對象鎖。
它倆是不沖突的,也就是說:獲取了類鎖的線程和獲取了對象鎖的線程是不沖突的!
可重入
public class Widget { // 鎖住了 public synchronized void doSomething() { } } public class LoggingWidget extends Widget { // 鎖住了 public synchronized void doSomething() { System.out.println(toString() + ": calling doSomething"); super.doSomething(); } }
當線程A進入到LoggingWidget的doSomething()方法時,此時拿到了LoggingWidget實例對象的鎖。
隨后在方法上又調用了父類Widget的doSomething()方法,它又是被synchronized修飾。
那現在我們LoggingWidget實例對象的鎖還沒有釋放,進入父類Widget的doSomething()方法還需要一把鎖嗎?
不需要的!
因為鎖的持有者是“線程”,而不是“調用”。線程A已經是有了LoggingWidget實例對象的鎖了,當再需要的時候可以繼續“開鎖”進去的!
這就是內置鎖的可重入性。
怎么釋放
Lock顯式鎖當方法(代碼塊)執行完畢后會自動釋放鎖,不需要做任何的操作。
當一個線程執行的代碼出現異常時,其所持有的鎖會自動釋放。
不會由于異常導致出現死鎖現象
Lock顯示鎖簡介
Lock顯式鎖是JDK1.5之后才有的,之前我們都是使用Synchronized鎖來使線程安全的
Lock顯式鎖是一個接口
Lock方式來獲取鎖支持中斷、超時不獲取、是非阻塞的
提高了語義化,哪里加鎖,哪里解鎖都得寫出來
Lock顯式鎖可以給我們帶來很好的靈活性,但同時我們必須手動釋放鎖
支持Condition條件對象
允許多個讀線程同時訪問共享資源
synchronized鎖和Lock鎖使用哪個
Lock鎖在剛出來的時候很多性能方面都比Synchronized鎖要好,但是從JDK1.6開始Synchronized鎖就做了各種的優化
優化操作:適應自旋鎖,鎖消除,鎖粗化,輕量級鎖,偏向鎖。
所以,到現在Lock鎖和Synchronized鎖的性能其實差別不是很大!而Synchronized鎖用起來又特別簡單。Lock鎖還得顧忌到它的特性,要手動釋放鎖才行(如果忘了釋放,這就是一個隱患)
所以說,我們絕大部分時候還是會使用Synchronized鎖,用到了Lock鎖提及的特性,帶來的靈活性才會考慮使用Lock顯式鎖
公平鎖ReentrantLock鎖公平鎖理解起來非常簡單:
線程將按照它們發出請求的順序來獲取鎖
非公平鎖就是:
線程發出請求的時可以“插隊”獲取鎖
Lock和synchronize都是默認使用非公平鎖的。如果不是必要的情況下,不要使用公平鎖
公平鎖會來帶一些性能的消耗的
AQS是ReentrantLock的基礎,AQS是構建鎖、同步器的框架
比synchronized更有伸縮性(靈活)
支持公平鎖(是相對公平的),支持非公平鎖
使用時最標準用法是在try之前調用lock方法,在finally代碼塊釋放鎖
class X { private final ReentrantLock lock = new ReentrantLock(); public void m() { lock.lock(); try { // ... method body } finally { lock.unlock() } } }ReentrantReadWriteLock鎖
synchronized內置鎖和ReentrantLock都是互斥鎖(一次只能有一個線程進入到臨界區(被鎖定的區域))
ReentrantReadWriteLock是一個讀寫鎖:
在讀取數據的時候,可以多個線程同時進入到到臨界區(被鎖定的區域)
在寫數據的時候,無論是讀線程還是寫線程都是互斥的
在讀的時候可以共享,在寫的時候是互斥的
讀鎖不支持條件對象,寫鎖支持條件對象
讀鎖不能升級為寫鎖,寫鎖可以降級為讀鎖
讀寫鎖也有公平和非公平模式
讀鎖支持多個讀線程進入臨界區,寫鎖是互斥的
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/73515.html
摘要:底層是是通過對象,對象有自己的對象頭,存儲了很多信息,其中一個信息標示是被哪個線程持有。當一個線程執行的代碼出現異常時,其所持有的鎖會自動釋放。 前言 回顧前面: 多線程三分鐘就可以入個門了! Thread源碼剖析 多線程基礎必要知識點!看了學習多線程事半功倍 只有光頭才能變強! 本文章主要講的是Java多線程加鎖機制,有兩種: Synchronized 顯式Lock 不得不嘮...
摘要:當一個線程持有重量級鎖時,另外一個線程就會被直接踢到同步隊列中等待。 java代碼先編譯成字節碼,字節碼最后編譯成cpu指令,因此Java的多線程實現最終依賴于jvm和cpu的實現 synchronized和volatile 我們先來討論一下volatile關鍵字的作用以及實現機制,每個線程看到的用volatile修飾的變量的值都是最新的,更深入的解釋就涉及到Java的內存模型了,我們...
摘要:并發機制與底層實現原理是輕量級的它在多處理器開發中保證了共享變量的可見性,因為它不會引起線程上下文的切換和調度,所以比的使用和執行成本更底。如果線程間存在鎖競爭,會帶來額外的鎖撤銷的消耗。輕量級鎖競爭的線程不會阻塞,提高了程序的響應速度。 java并發機制與底層實現原理 volatile volatile是輕量級的synchronize,它在多處理器開發中保證了共享變量的可見性,因為它...
摘要:在這個等待通知機制中,我們需要考慮以下四個要素。何時等待線程要求的條件不滿足就等待。是會隨機地通知等待隊列中的一個線程,而會通知等待隊列中的所有線程。 由上一篇文章你應該已經知道,在 破壞占用且等待條件 的時候,如果轉出賬本和轉入賬本不滿足同時在文件架上這個條件,就用死循環的方式來循環等待,核心代碼如下: // 一次性申請轉出賬戶和轉入賬戶,直到成功 while(!actr.apply...
閱讀 3485·2021-09-02 09:53
閱讀 1805·2021-08-26 14:13
閱讀 2767·2019-08-30 15:44
閱讀 1328·2019-08-30 14:03
閱讀 1978·2019-08-26 13:42
閱讀 3026·2019-08-26 12:21
閱讀 1315·2019-08-26 11:54
閱讀 1909·2019-08-26 10:46