日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > java >内容正文

java

java+queue+se_「013期」JavaSE面试题(十三):多线程(3)

發(fā)布時(shí)間:2025/4/5 java 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java+queue+se_「013期」JavaSE面试题(十三):多线程(3) 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

本文轉(zhuǎn)載自【微信公眾號(hào):java進(jìn)階架構(gòu)師,ID:java_jiagoushi】經(jīng)微信公眾號(hào)授權(quán)轉(zhuǎn)載,如需轉(zhuǎn)載與原文作者聯(lián)系

開(kāi)篇介紹

大家好,我是Java面試題庫(kù)的提褲姐,今天這篇是JavaSE系列的第十一篇,主要總結(jié)了Java中的多線程問(wèn)題,多線程分為三篇來(lái)講,這篇是第三篇,在后續(xù),會(huì)沿著第一篇開(kāi)篇的知識(shí)線路一直總結(jié)下去,做到日更!如果我能做到百日百更,希望你也可以跟著百日百刷,一百天養(yǎng)成一個(gè)好習(xí)慣。

Q:

volatile關(guān)鍵字的作用?

對(duì)于可見(jiàn)性,Java提供了volatile關(guān)鍵字來(lái)保證可見(jiàn)性。當(dāng)一個(gè)共享變量被volatile修飾時(shí),它會(huì)保證修改的值會(huì)立即被更新到主存,當(dāng)有其他線程需要讀取時(shí),它會(huì)去內(nèi)存中讀取新值。主要的原理是使用了內(nèi)存指令。

LoadLoad重排序:一個(gè)處理器先執(zhí)行一個(gè)L1讀操作,再執(zhí)行一個(gè)L2讀操作;但是另外一個(gè)處理器看到的是先L2再L1StoreStore重排序:一個(gè)處理器先執(zhí)行一個(gè)W1寫(xiě)操作,再執(zhí)行一個(gè)W2寫(xiě)操作;但是另外一個(gè)處理器看到的是先W2再W1LoadStore重排序:一個(gè)處理器先執(zhí)行一個(gè)L1讀操作,再執(zhí)行一個(gè)W2寫(xiě)操作;但是另外一個(gè)處理器看到的是先W2再L1StoreLoad重排序:一個(gè)處理器先執(zhí)行一個(gè)W1寫(xiě)操作,再執(zhí)行一個(gè)L2讀操作;但是另外一個(gè)處理器看到的是先L2再W1

Q:

說(shuō)一下volatile關(guān)鍵字對(duì)原子性、可見(jiàn)性以及有序性的保證?

在volatile變量寫(xiě)操作的前面會(huì)加入一個(gè)

Release

屏障,然后在之后會(huì)加入一個(gè)

Store

屏障,這樣就可以保證volatile寫(xiě)跟Release屏障之前的任何讀寫(xiě)操作都不會(huì)指令重排,然后Store屏障保證了,寫(xiě)完數(shù)據(jù)之后,立馬會(huì)執(zhí)行flush處理器緩存的操作。

在volatile變量讀操作的前面會(huì)加入一個(gè)

Load

屏障,這樣就可以保證對(duì)這個(gè)變量的讀取時(shí),如果被別的處理器修改過(guò)了,必須得從其他 處理器的高速緩存(或者主內(nèi)存)中加載到自己本地高速緩存里,保證讀到的是最新數(shù)據(jù);在之后會(huì)加入一個(gè)

Acquire

屏障,禁止volatile讀操作之后的任何讀寫(xiě)操作會(huì)跟volatile讀指令重排序。與volatie讀寫(xiě)內(nèi)存屏障對(duì)比一下,是類(lèi)似的意思。

Acquire屏障

其實(shí)就是

LoadLoad屏障 + LoadStore屏障

Release屏障

其實(shí)就是

StoreLoad屏障 + StoreStore屏障

Q:

什么是CAS?

CAS(compare and swap)的縮寫(xiě)。Java利用CPU的CAS指令,同時(shí)借助JNI來(lái)完成Java的非阻塞算法,實(shí)現(xiàn)原子操作。其它原子操作都是利用類(lèi)似的特性完成的。 CAS有3個(gè)操作數(shù):

內(nèi)存值V

舊的預(yù)期值A(chǔ)

要修改的新值B

。當(dāng)且僅當(dāng)預(yù)期值A(chǔ)和內(nèi)存值V相同時(shí),將內(nèi)存值V修改為B,否則什么都不做。CAS的缺點(diǎn):

CPU開(kāi)銷(xiāo)過(guò)大在并發(fā)量比較高的情況下,如果許多線程反復(fù)嘗試更新某一個(gè)變量,卻又一直更新不成功,循環(huán)往復(fù),會(huì)給CPU帶來(lái)很到的壓力。不能保證代碼塊的原子性CAS機(jī)制所保證的知識(shí)一個(gè)變量的原子性操作,而不能保證整個(gè)代碼塊的原子性。比如需要保證3個(gè)變量共同進(jìn)行原子性的更新,就不得不使用synchronized了。ABA問(wèn)題這是CAS機(jī)制最大的問(wèn)題所在。

Q:

什么是AQS?

AQS,即AbstractQueuedSynchronizer,

隊(duì)列同步器

,它是Java并發(fā)用來(lái)構(gòu)建鎖和其他同步組件的基礎(chǔ)框架。

同步組件對(duì)AQS的使用: AQS是一個(gè)抽象類(lèi),主是是以繼承的方式使用。AQS本身是沒(méi)有實(shí)現(xiàn)任何同步接口的,它僅僅只是定義了同步狀態(tài)的獲取和釋放的方法來(lái)供自定義的同步組件的使用。查看源碼可知,在java的同步組件中,AQS的子類(lèi)(Sync等)一般是同步組件的靜態(tài)內(nèi)部類(lèi),即通過(guò)組合的方式使用。 抽象的隊(duì)列式的同步器,AQS定義了一套多線程訪問(wèn)共享資源的同步器框架,許多同步類(lèi)實(shí)現(xiàn)都依賴(lài)于它,如常用的ReentrantLock/Semaphore/CountDownLatch 它維護(hù)了一個(gè)volatile int state(代表共享資源)和一個(gè)FIFO(雙向隊(duì)列)線程等待隊(duì)列(多線程爭(zhēng)用資源被阻塞時(shí)會(huì)進(jìn)入此隊(duì)列)

1public class CountDownLatch {2 /** 3 * Synchronization control For CountDownLatch. 4 * Uses AQS state to represent count. 5 */ 6 private static final class Sync extends AbstractQueuedSynchronizer { 7 private static final long serialVersionUID = 4982264981922014374L; 8 9 Sync(int count) {10 setState(count);11 }1213 int getCount() {14 return getState();15 }1617 protected int tryAcquireShared(int acquires) {18 return (getState() == 0) ? 1 : -1;19 }2021 protected boolean tryReleaseShared(int releases) {22 // Decrement count; signal when transition to zero23 for (;;) {24 int c = getState();25 if (c == 0)26 return false;27 int nextc = c-1;28 if (compareAndSetState(c, nextc))29 return nextc == 0;30 }31 }32 }33}

Q:

Semaphore是什么?

Semaphore就是一個(gè)信號(hào)量,它的作用是限制某段代碼塊的并發(fā)數(shù)。 semaphore有一個(gè)構(gòu)造函數(shù),可以傳入一個(gè)int型整數(shù)n,表示某段代碼最多只有n個(gè)線程可以訪問(wèn),如果超出了n,那么請(qǐng)等待,等到某個(gè)線程執(zhí)行完畢這段代碼塊,下一個(gè)線程再進(jìn)入。由此可以看出如果Semaphore構(gòu)造函數(shù)中傳入的int型整數(shù)n=1,相當(dāng)于變成了一個(gè)synchronized了。

1public static void main(String[] args) { 2 int N = 8; //工人數(shù) 3 Semaphore semaphore = new Semaphore(5); //機(jī)器數(shù)目 4 for(int i=0;i

Q:

Synchronized的原理是什么?

Synchronized是由JVM實(shí)現(xiàn)的一種實(shí)現(xiàn)互斥同步的方式,查看被Synchronized修飾過(guò)的程序塊編譯后的字節(jié)碼,會(huì)發(fā)現(xiàn),被Synchronized修飾過(guò)的程序塊,在編譯前后被編譯器生成了

monitorenter

monitorexit

兩個(gè)字節(jié)碼指令。在虛擬機(jī)執(zhí)行到monitorenter指令時(shí),首先要嘗試獲取對(duì)象的鎖:如果這個(gè)對(duì)象沒(méi)有鎖定,或者當(dāng)前線程已經(jīng)擁有了這個(gè)對(duì)象的鎖,把鎖的計(jì)數(shù)器+1;當(dāng)執(zhí)行monitorexit指令時(shí),將鎖計(jì)數(shù)器-1;當(dāng)計(jì)數(shù)器為0時(shí),鎖就被釋放了。如果獲取對(duì)象失敗了,那當(dāng)前線程就要阻塞等待,直到對(duì)象鎖被另外一個(gè)線程釋放為止。Java中Synchronize通過(guò)在對(duì)象頭設(shè)置標(biāo)志,達(dá)到了獲取鎖和釋放鎖的目的。

Q:

為什么說(shuō)Synchronized是非公平鎖?

非公平主要表現(xiàn)在獲取鎖的行為上,并非是按照申請(qǐng)鎖的時(shí)間前后給等待線程分配鎖的,每當(dāng)鎖被釋放后,任何一個(gè)線程都有機(jī)會(huì)競(jìng)爭(zhēng)到鎖,這樣做的目的是為了提高執(zhí)行性能,缺點(diǎn)是可能會(huì)產(chǎn)生線程饑餓現(xiàn)象。

Q:

JVM對(duì)java的原生鎖做了哪些優(yōu)化?

在Java6之前, Monitor的實(shí)現(xiàn)完全依賴(lài)底層操作系統(tǒng)的互斥鎖來(lái)實(shí)現(xiàn).

由于Java層面的線程與操作系統(tǒng)的原生線程有映射關(guān)系,如果要將一個(gè)線程進(jìn)行阻塞或喚起都需要操作系統(tǒng)的協(xié)助,這就需要從用戶(hù)態(tài)切換到內(nèi)核態(tài)來(lái)執(zhí)行,這種切換代價(jià)十分昂貴,很耗處理器時(shí)間,現(xiàn)代JDK中做了大量的優(yōu)化。一種優(yōu)化是使用

自旋鎖

,即在把線程進(jìn)行阻塞操作之前先讓線程自旋等待一段時(shí)間,可能在等待期間其他線程已經(jīng)解鎖,這時(shí)就無(wú)需再讓線程執(zhí)行阻塞操作,避免了用戶(hù)態(tài)到內(nèi)核態(tài)的切換。現(xiàn)代JDK中還提供了三種不同的 Monitor實(shí)現(xiàn),也就是三種不同的鎖:

偏向鎖(Biased Locking)輕量級(jí)鎖重量級(jí)鎖這三種鎖使得JDK得以?xún)?yōu)化 Synchronized的運(yùn)行,當(dāng)JVM檢測(cè)到不同的競(jìng)爭(zhēng)狀況時(shí),會(huì)自動(dòng)切換到適合的鎖實(shí)現(xiàn),這就是鎖的升級(jí)、降級(jí)。當(dāng)沒(méi)有競(jìng)爭(zhēng)出現(xiàn)時(shí),默認(rèn)會(huì)使用偏向鎖。JVM會(huì)利用CAS操作,在對(duì)象頭上的 Mark Word部分設(shè)置線程ID,以表示這個(gè)對(duì)象偏向于當(dāng)前線程,所以并不涉及真正的互斥鎖,因?yàn)樵诤芏鄳?yīng)用場(chǎng)景中,大部分對(duì)象生命周期中最多會(huì)被一個(gè)線程鎖定,使用偏向鎖可以降低無(wú)競(jìng)爭(zhēng)開(kāi)銷(xiāo)。如果有另一線程試圖鎖定某個(gè)被偏向過(guò)的對(duì)象,JVM就撤銷(xiāo)偏向鎖,切換到輕量級(jí)鎖實(shí)現(xiàn)。輕量級(jí)鎖依賴(lài)CAS操作 Mark Word來(lái)試圖獲取鎖,如果重試成功,就使用普通的輕量級(jí)鎖否則,進(jìn)一步升級(jí)為重量級(jí)鎖。

Q:

Synchronized和 ReentrantLock的異同?

synchronized:是java內(nèi)置的關(guān)鍵字,它提供了一種獨(dú)占的加鎖方式。synchronized的獲取和釋放鎖由JVM實(shí)現(xiàn),用戶(hù)不需要顯示的釋放鎖,非常方便。然而synchronized也有一些問(wèn)題:當(dāng)線程嘗試獲取鎖的時(shí)候,如果獲取不到鎖會(huì)一直阻塞。如果獲取鎖的線程進(jìn)入休眠或者阻塞,除非當(dāng)前線程異常,否則其他線程嘗試獲取鎖必須一直等待。

ReentrantLock:ReentrantLock是Lock的實(shí)現(xiàn)類(lèi),是一個(gè)互斥的同步鎖。ReentrantLock是JDK 1.5之后提供的API層面的互斥鎖,需要lock()和unlock()方法配合try/finally語(yǔ)句塊來(lái)完成。等待可中斷避免,出現(xiàn)死鎖的情況(如果別的線程正持有鎖,會(huì)等待參數(shù)給定的時(shí)間,在等待的過(guò)程中,如果獲取了鎖定,就返回true,如果等待超時(shí),返回false) 公平鎖與非公平鎖多個(gè)線程等待同一個(gè)鎖時(shí),必須按照申請(qǐng)鎖的時(shí)間順序獲得鎖,Synchronized鎖非公平鎖,ReentrantLock默認(rèn)的構(gòu)造函數(shù)是創(chuàng)建的非公平鎖,可以通過(guò)參數(shù)true設(shè)為公平鎖,但公平鎖表現(xiàn)的性能不是很好。

從功能角度:ReentrantLock比 Synchronized的同步操作更精細(xì)(因?yàn)榭梢韵衿胀▽?duì)象一樣使用),甚至實(shí)現(xiàn) Synchronized沒(méi)有的高級(jí)功能,如:

等待可中斷當(dāng)持有鎖的線程長(zhǎng)期不釋放鎖的時(shí)候,正在等待的線程可以選擇放棄等待,對(duì)處理執(zhí)行時(shí)間非常長(zhǎng)的同步塊很有用。帶超時(shí)的獲取鎖嘗試在指定的時(shí)間范圍內(nèi)獲取鎖,如果時(shí)間到了仍然無(wú)法獲取則返回??梢耘袛嗍欠裼芯€程在排隊(duì)等待獲取鎖??梢皂憫?yīng)中斷請(qǐng)求與Synchronized不同,當(dāng)獲取到鎖的線程被中斷時(shí),能夠響應(yīng)中斷,中斷異常將會(huì)被拋出,同時(shí)鎖會(huì)被釋放??梢詫?shí)現(xiàn)公平鎖。從鎖釋放角度:Synchronized在JVM層面上實(shí)現(xiàn)的,不但可以通過(guò)一些監(jiān)控工具監(jiān)控 Synchronized的鎖定,而且在代碼執(zhí)行出現(xiàn)異常時(shí),JVM會(huì)自動(dòng)釋放鎖定,但是使用Lock則不行,Lock是通過(guò)代碼實(shí)現(xiàn)的,要保證鎖定一定會(huì)被釋放,就必須將unLock()放到finally{}中。

從性能角度:Synchronized早期實(shí)現(xiàn)比較低效,對(duì)比 ReentrantLock,大多數(shù)場(chǎng)景性能都相差較大。但是在Java6中對(duì)其進(jìn)行了非常多的改進(jìn),在競(jìng)爭(zhēng)不激烈時(shí):Synchronized的性能要優(yōu)于 ReetrantLock;在高競(jìng)爭(zhēng)情況下:Synchronized的性能會(huì)下降幾十倍,但是 ReetrantLock的性能能維持常態(tài)。

總結(jié)

以上是生活随笔為你收集整理的java+queue+se_「013期」JavaSE面试题(十三):多线程(3)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。