自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

面試官:有了解過(guò)Synchronized嗎 說(shuō)說(shuō)看

開(kāi)發(fā) 后端
在Java中,鎖基本上都是基于對(duì)象而言的,所以又稱(chēng)為對(duì)象鎖, 一個(gè)類(lèi)通常只有一個(gè)class對(duì)象和n個(gè)實(shí)例對(duì)象,它們共享class對(duì)象,而我們有時(shí)候會(huì)對(duì)class對(duì)象加鎖,所以又稱(chēng)為class對(duì)象鎖。

前言

相信很多同學(xué)對(duì)synchronized的使用上不陌生,之前也給大家講解過(guò)它的使用。本篇主要帶大家深入了解一下它,大家也可以自己試著總結(jié)一下,這也是面試中常常問(wèn)到的,單純的回答它的基本使用,是驚艷不到面試官的~。

synchronized 介紹

從字面意思翻譯過(guò)來(lái)就是同步的意思,所以它也叫同步鎖,我們通常會(huì)給某個(gè)方法或者某塊代碼加上Synchronized鎖來(lái)解決多線程中并發(fā)帶來(lái)的問(wèn)題,它也是最常用,最簡(jiǎn)單的一種方法。

在Java中,鎖基本上都是基于對(duì)象而言的,所以又稱(chēng)為對(duì)象鎖, 一個(gè)類(lèi)通常只有一個(gè)class對(duì)象和n個(gè)實(shí)例對(duì)象,它們共享class對(duì)象,而我們有時(shí)候會(huì)對(duì)class對(duì)象加鎖,所以又稱(chēng)為class對(duì)象鎖。

這里大家要注意的是對(duì)象需要是一個(gè)非null的對(duì)象,我們通常也叫做對(duì)象監(jiān)視器(Object Monitor)。

重量級(jí)鎖

在JDK 1.5之前,它是一個(gè)重量級(jí)鎖,我們通常都會(huì)使用它來(lái)保證線程同步。在1.5的時(shí)候還提供了一個(gè)Lock接口來(lái)實(shí)現(xiàn)同步鎖的功能,我們只需要顯式的獲取鎖和釋放鎖。

重在哪?

在1.5的時(shí)候,Synchronized它依賴(lài)于操作系統(tǒng)底層的Mutex Lock實(shí)現(xiàn),每次釋放鎖和獲取鎖都會(huì)導(dǎo)致用戶(hù)態(tài)和內(nèi)核態(tài)的切換,從而增加系統(tǒng)性能的開(kāi)銷(xiāo),當(dāng)出現(xiàn)大并發(fā)的情況下,鎖競(jìng)爭(zhēng)會(huì)比較激烈,性能顯得非常糟糕,所以稱(chēng)為重量級(jí)鎖,所以大家往往會(huì)選擇Lock鎖。

鎖優(yōu)化

但是Synchronized又是那么的簡(jiǎn)單好用,又是官方自帶的,怎么可能放棄呢?所以在1.6之后,引入了大量的鎖優(yōu)化,比如自旋鎖,輕量級(jí)鎖, 偏向鎖等,下面我們逐個(gè)看一下。

synchronized 實(shí)現(xiàn)原理

我們了解鎖優(yōu)化之前,我們先看一下它的實(shí)現(xiàn)原理。

首先我們看下同步塊中,因?yàn)樗顷P(guān)鍵字,我們看不到源碼實(shí)現(xiàn),所以只能反編譯看一下,通過(guò) javap -v **.class。

public static void main(String[] args) {
synchronized(Demo.class) {
System.out.println("hello");
}
}
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=3, args_size=1
0: ldc #2 // class com/thread/base/Demo
2: dup
3: astore_1
4: monitorenter
5: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
8: ldc #4 // String hello
10: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
13: aload_1
14: monitorexit
15: goto 23
18: astore_2
19: aload_1
20: monitorexit
21: aload_2
22: athrow
23: return

我們重點(diǎn)關(guān)注monitorenter和monitorexit,那么他倆是什么意思呢?

monitorenter,如果當(dāng)前 monitor 的進(jìn)入數(shù)為 0 時(shí),線程就會(huì)進(jìn)入 monitor,并且把進(jìn)入數(shù) + 1,那么該線程就是 monitor 的擁有者 (owner)。如果該線程已經(jīng)是 monitor 的擁有者,又重新進(jìn)入,就會(huì)把進(jìn)入數(shù)再次 + 1。也就是可重入。

monitorexit,執(zhí)行 monitorexit 的線程必須是 monitor 的擁有者,指令執(zhí)行后,monitor 的進(jìn)入數(shù)減 1,如果減 1 后進(jìn)入數(shù)為 0,則該線程會(huì)退出 monitor。其他被阻塞的線程就可以嘗試去獲取 monitor 的所有權(quán)。指令出現(xiàn)了兩次,第 1 次為同步正常退出釋放鎖;第2次為發(fā)生異步退出釋放鎖。

我們?cè)賮?lái)看一下, 修飾實(shí)例方法中的表現(xiàn):

class Demo {
public synchronized void hello() {
System.out.println("hello");
}
}
public synchronized void hello();
descriptor: ()V
flags: ACC_PUBLIC, ACC_SYNCHRONIZED
Code:
stack=2, locals=1, args_size=1
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3 // String hello
5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
LineNumberTable:
line 25: 0
line 26: 8
LocalVariableTable:
Start Length Slot Name Signature
0 9 0 this Lcom/thread/base/Demo;
}

我們重點(diǎn)關(guān)注ACC_SYNCHRONIZED,它作用就是一旦執(zhí)行到這個(gè)方法時(shí),就會(huì)先判斷是否有標(biāo)志位,如果有,就會(huì)先嘗試獲取 monitor,獲取成功才能執(zhí)行方法,方法執(zhí)行完成后再釋放 monitor。在方法執(zhí)行期間,其他線程都無(wú)法獲取同一個(gè) monitor。歸根結(jié)底還是對(duì) monitor 對(duì)象的爭(zhēng)奪,只是同步方法是一種隱式的方式來(lái)實(shí)現(xiàn)。

synchronized 在 JVM 里的實(shí)現(xiàn)就是基于進(jìn)入和退出 monitor 來(lái)實(shí)現(xiàn)的,底層則是通過(guò)成對(duì)的 MonitorEnter 和 MonitorExit 指令來(lái)實(shí)現(xiàn)。

有了以上的認(rèn)識(shí),下面我們就看看鎖優(yōu)化。

Synchronized中的鎖優(yōu)化

自適應(yīng)自旋鎖

自旋鎖,之前我們講FutureTask源碼的時(shí)候,有一個(gè)內(nèi)部方法awaitDone(),給大家有介紹過(guò),就是基于它實(shí)現(xiàn)的,今天再給大家總結(jié)一下。

它的目的是為了避免阻塞和喚醒的切換,在沒(méi)有獲得鎖的時(shí)候就不進(jìn)入阻塞,不斷地循環(huán)檢測(cè)鎖是否被釋放。但是,它也有弊端,我們通常來(lái)講,一個(gè)線程占用鎖的時(shí)間相對(duì)較短,但是萬(wàn)一占用很長(zhǎng)時(shí)間怎么辦?這樣會(huì)占用大量cpu時(shí)間,這樣會(huì)導(dǎo)致性能變差,所以在1.6引入了自適應(yīng)自旋鎖來(lái)滿(mǎn)足這樣的場(chǎng)景。

那么什么是自適應(yīng)自旋鎖呢?自旋的次數(shù)不是固定的,而是由前一次在同一個(gè)鎖上的自旋時(shí)間及鎖的擁有者的狀態(tài)來(lái)決定。如果此次自旋成功了,很有可能下一次也能成功,于是允許自旋的次數(shù)就會(huì)更多,反過(guò)來(lái)說(shuō),如果很少有線程能夠自旋成功,很有可能下一次也是失敗,則自旋次數(shù)就更少。這樣一來(lái),就能夠更好的利用系統(tǒng)資源。

鎖消除

鎖消除是一種鎖的優(yōu)化策略,這種優(yōu)化更加徹底,在 JVM 編譯時(shí),通過(guò)對(duì)運(yùn)行上下文的掃描,去除不可能存在共享資源競(jìng)爭(zhēng)的鎖。這種優(yōu)化策略可以消除沒(méi)有必要的鎖,去除獲取鎖的時(shí)間。

鎖粗化

如果一系列的連續(xù)加鎖解鎖操作,可能會(huì)導(dǎo)致不必要的性能損耗,所以引入鎖粗話(huà)的概念。意思是將多個(gè)連續(xù)加鎖、解鎖的操作連接在一起,擴(kuò)展成為一個(gè)范圍更大的鎖, 這個(gè)應(yīng)該很好理解。

偏向鎖

偏向鎖是JDK 1.6引入的,它解決的場(chǎng)景是什么呢?我們大部分使用鎖都是解決多線程場(chǎng)景下的問(wèn)題,但有時(shí)候往往一個(gè)線程也會(huì)存在這樣的問(wèn)題,偏向鎖是在單線程執(zhí)行代碼塊時(shí)使用的機(jī)制。

鎖的爭(zhēng)奪實(shí)際上是 Monitor 對(duì)象的爭(zhēng)奪,還有每個(gè)對(duì)象都有一個(gè)對(duì)象頭,對(duì)象頭是由 Mark Word 和 Klass pointer 組成的。一旦有線程持有了這個(gè)鎖對(duì)象,標(biāo)志位修改為 1,就進(jìn)入偏向模式,同時(shí)會(huì)把這個(gè)線程的 ID 記錄在對(duì)象的 Mark Word 中,當(dāng)同一個(gè)線程再次進(jìn)入時(shí),就不再進(jìn)行同步操作,大大減少了鎖獲取的時(shí)間,從而提高了性能。

輕量級(jí)鎖

我們上邊提到的偏向鎖,在多線程情況下如果偏向鎖失敗就會(huì)升級(jí)為輕量級(jí)鎖, Mark Word 的結(jié)構(gòu)也變?yōu)檩p量級(jí)鎖的結(jié)構(gòu)。

執(zhí)行同步代碼塊之前,JVM 會(huì)在線程的棧幀中創(chuàng)建一個(gè)鎖記錄(Lock Record),并將 Mark Word 拷貝復(fù)制到鎖記錄中。然后嘗試通過(guò) CAS 操作將 Mark Word 中的鎖記錄的指針,指向創(chuàng)建的 Lock Record。如果成功表示獲取鎖狀態(tài)成功,如果失敗,則進(jìn)入自旋獲取鎖狀態(tài)。

如果自旋鎖失敗,就會(huì)升級(jí)為重量級(jí)鎖,也就是我們之前講的,會(huì)把線程阻塞,需等待喚醒。

重量級(jí)鎖

它又稱(chēng)為悲觀鎖, 升級(jí)到這種情況下,鎖競(jìng)爭(zhēng)比較激烈,占用時(shí)間也比較長(zhǎng),為了減少cpu的消耗,會(huì)將線程阻塞,進(jìn)入阻塞隊(duì)列。

synchronized就是通過(guò)鎖升級(jí)策略來(lái)適應(yīng)不同的場(chǎng)景,所以現(xiàn)在synchronized被優(yōu)化的很好,也是我們項(xiàng)目中往往都會(huì)使用它的理由。

結(jié)束語(yǔ)

本節(jié)的內(nèi)容比較多,大家好好理解,特別是鎖的升級(jí)策略。本節(jié)我們提到了Lock鎖,下一節(jié),帶大家深入學(xué)習(xí)一下Java的Lock 。

責(zé)任編輯:姜華 來(lái)源: 今日頭條
相關(guān)推薦

2022-06-09 11:20:44

volatile關(guān)鍵字

2022-06-15 15:14:17

Java公平鎖非公平鎖

2022-07-26 08:40:42

Java并發(fā)工具類(lèi)

2022-08-02 06:31:32

Java并發(fā)工具類(lèi)

2022-07-11 10:47:46

容器JAVA

2022-06-30 08:14:05

Java阻塞隊(duì)列

2022-06-08 13:54:23

指令重排Java

2020-10-08 14:15:15

Zookeeper

2022-06-30 14:31:57

Java阻塞隊(duì)列

2025-04-01 00:00:00

項(xiàng)目CRUD單例模式

2022-06-24 06:43:57

線程池線程復(fù)用

2022-07-18 14:18:26

Babel代碼面試

2021-08-11 08:53:23

Git命令面試

2024-08-22 10:39:50

@Async注解代理

2024-03-05 10:33:39

AOPSpring編程

2024-05-30 08:04:20

Netty核心組件架構(gòu)

2023-12-13 13:31:00

useEffect對(duì)象瀏覽器

2021-08-02 08:34:20

React性能優(yōu)化

2021-06-02 09:42:29

Node. js全局對(duì)象

2021-05-27 05:37:10

HTTP請(qǐng)求頭瀏覽器
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)