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