1、ABA問題及解決?法簡述。
CAS 算法是基于值來做?較的,如果當(dāng)前有兩個(gè)線程,?個(gè)線程將變量值從 A 改為 B ,再由 B 改回為 A,當(dāng)前線程開始執(zhí)? CAS 算法時(shí),就很容易認(rèn)為值沒有變化,誤認(rèn)為讀取數(shù)據(jù)到執(zhí)? CAS 算法的期間,沒有線程修改過數(shù)據(jù)。
juc 包提供了?個(gè) AtomicStampedReference,即在原始的版本下加?版本號戳,解決 ABA 問題。
2、簡述常?的Atomic類。
在很多時(shí)候,我們需要的僅僅是?個(gè)簡單的、?效的、線程安全的++或者–?案,使?synchronized關(guān)鍵字和lock固然可以實(shí)現(xiàn),但代價(jià)?較?,此時(shí)?原?類更加?便。
基本數(shù)據(jù)類型的原?類有:
(1)AtomicInteger 原?更新整形;
(2)AtomicLong 原?更新?整型;
(3)AtomicBoolean 原?更新布爾類型。
Atomic數(shù)組類型有:
(1)AtomicIntegerArray 原?更新整形數(shù)組?的元素;
(2)AtomicLongArray 原?更新?整型數(shù)組?的元素;
(3)AtomicReferenceArray 原?更新引?類型數(shù)組?的元素。
Atomic引?類型有:
(1)AtomicReference 原?更新引?類型;
(2)AtomicMarkableReference 原?更新帶有標(biāo)記位的引?類型,可以綁定?個(gè) boolean 標(biāo)記;
(3)AtomicStampedReference 原?更新帶有版本號的引?類型。
FieldUpdater類型:
(1)AtomicIntegerFieldUpdater 原?更新整形字段的更新器;
(2)AtomicLongFieldUpdater 原?更新?整形字段的更新器;
(3)AtomicReferenceFieldUpdater 原?更新引?類型字段的更新器。
3、簡述Atomic類基本實(shí)現(xiàn)原理。
以AtomicIntger 為例。?法getAndIncrement,以原??式將當(dāng)前的值加1,具體實(shí)現(xiàn)為:
(1)在 for 死循環(huán)中取得 AtomicInteger ?存儲的數(shù)值
(2)對 AtomicInteger 當(dāng)前的值加 1
(3)調(diào)? compareAndSet ?法進(jìn)?原?更新
(4)先檢查當(dāng)前數(shù)值是否等于 expect
(5)如果等于則說明當(dāng)前值沒有被其他線程修改,則將值更新為 next,
(6)如果不是會(huì)更新失敗返回 false,程序會(huì)進(jìn)? for 循環(huán)重新進(jìn)? compareAndSet 操作。
4、簡述CountDownLatch。
CountDownLatch這個(gè)類使?個(gè)線程等待其他線程各?執(zhí)?完畢后再執(zhí)?。是通過?個(gè)計(jì)數(shù)器來實(shí)現(xiàn)的,計(jì)數(shù)器的初始值是線程的數(shù)量。每當(dāng)?個(gè)線程執(zhí)?完畢后,調(diào)?countDown?法,計(jì)數(shù)器的值就減1,當(dāng)計(jì)數(shù)器的值為0時(shí),表示所有線程都執(zhí)?完畢,然后在等待的線程就可以恢復(fù)?作了。只能?次性使?,不能reset。
5、簡述CyclicBarrier。
CyclicBarrier 主要功能和CountDownLatch類似,也是通過?個(gè)計(jì)數(shù)器,使?個(gè)線程等待其他線程各?執(zhí)?完畢后再執(zhí)?。但是其可以重復(fù)使?(reset)。
6、簡述Semaphore。
Semaphore即信號量。Semaphore 的構(gòu)造?法參數(shù)接收?個(gè) int 值,設(shè)置?個(gè)計(jì)數(shù)器,表示可?的許可數(shù)量即最?并發(fā)數(shù)。使? acquire ?法獲得?個(gè)許可證,計(jì)數(shù)器減?,使? release ?法歸還許可,計(jì)數(shù)器加?。如果此時(shí)計(jì)數(shù)器值為0,線程進(jìn)?休眠。
7、簡述Exchanger。
Exchanger類可?于兩個(gè)線程之間交換信息。可簡單地將Exchanger對象理解為?個(gè)包含兩個(gè)格?的容器,通過exchanger?法可以向兩個(gè)格?中填充信息。線程通過exchange ?法交換數(shù)據(jù),第?個(gè)線程執(zhí)?exchange ?法后會(huì)阻塞等待第?個(gè)線程執(zhí)?該?法。當(dāng)兩個(gè)線程都到達(dá)同步點(diǎn)時(shí)這兩個(gè)線程就可以交換數(shù)據(jù)當(dāng)兩個(gè)格?中的均被填充時(shí),該對象會(huì)?動(dòng)將兩個(gè)格?的信息交換,然后返回給線程,從?實(shí)現(xiàn)兩個(gè)線程的信息交換。
8、簡述ConcurrentHashMap。
JDK7采?鎖分段技術(shù)。?先將數(shù)據(jù)分成 Segment 數(shù)據(jù)段,然后給每?個(gè)數(shù)據(jù)段配?把鎖,當(dāng)?個(gè)線程占?鎖訪問其中?個(gè)段的數(shù)據(jù)時(shí),其他段的數(shù)據(jù)也能被其他線程訪問。
get 除讀到空值不需要加鎖。該?法先經(jīng)過?次再散列,再?這個(gè)散列值通過散列運(yùn)算定位到 Segment,最后通過散列算法定位到元素。put 須加鎖,?先定位到 Segment,然后進(jìn)?插?操作,第?步判斷是否需要對 Segment ?的 HashEntry 數(shù)組進(jìn)?擴(kuò)容,第?步定位添加元素的位置,然后將其放?數(shù)組。
JDK8的改進(jìn):
(1)取消分段鎖機(jī)制,采?CAS算法進(jìn)?值的設(shè)置,如果CAS失敗再使? synchronized 加鎖添加元素;
(2)引?紅?樹結(jié)構(gòu),當(dāng)某個(gè)槽內(nèi)的元素個(gè)數(shù)超過8且 Node數(shù)組 容量?于 64 時(shí),鏈表轉(zhuǎn)為紅?樹;
(3)使?了更加優(yōu)化的?式統(tǒng)計(jì)集合內(nèi)的元素?cái)?shù)量。
9、synchronized底層實(shí)現(xiàn)原理。
Java 對象底層都會(huì)關(guān)聯(lián)?個(gè) monitor,使? synchronized 時(shí) JVM 會(huì)根據(jù)使?環(huán)境找到對象的 monitor,根據(jù) monitor 的狀態(tài)進(jìn)?加解鎖的判斷。如果成功加鎖就成為該 monitor 的唯?持有者,monitor 在被釋放前不能再被其他線程獲取。
synchronized在JVM編譯后會(huì)產(chǎn)?monitorenter 和 monitorexit 這兩個(gè)字節(jié)碼指令,獲取和釋放 monitor。
這兩個(gè)字節(jié)碼指令都需要?個(gè)引?類型的參數(shù)指明要鎖定和解鎖的對象,對于同步普通?法,鎖是當(dāng)前實(shí)例對象;對于靜態(tài)同步?法,鎖是當(dāng)前類的 Class 對象;對于同步?法塊,鎖是synchronized 括號?的對象。
執(zhí)? monitorenter 指令時(shí),?先嘗試獲取對象鎖。如果這個(gè)對象沒有被鎖定,或當(dāng)前線程已經(jīng)持有鎖,就把鎖的計(jì)數(shù)器加 1,執(zhí)? monitorexit 指令時(shí)會(huì)將鎖計(jì)數(shù)器減 1。?旦計(jì)數(shù)器為 0 鎖隨即就被釋放。
10、synchronized關(guān)鍵詞使??法。
(1)直接修飾某個(gè)實(shí)例?法;
(2)直接修飾某個(gè)靜態(tài)?法;
(3)修飾代碼塊。
11、簡述Java偏向鎖。
JDK 1.6 中提出了偏向鎖的概念。該鎖提出的原因是,開發(fā)者發(fā)現(xiàn)多數(shù)情況下鎖并不存在競爭,?把鎖往往是由同?個(gè)線程獲得的。偏向鎖并不會(huì)主動(dòng)釋放,這樣每次偏向鎖進(jìn)?的時(shí)候都會(huì)判斷該資源是否是偏向??的,如果是偏向??的則不需要進(jìn)?額外的操作,直接可以進(jìn)?同步操作。
其申請流程為:
(1)?先需要判斷對象的 Mark Word 是否屬于偏向模式,如果不屬于,那就進(jìn)?輕量級鎖判斷邏輯。否則繼續(xù)下?步判斷;
(2)判斷?前請求鎖的線程 ID 是否和偏向鎖本身記錄的線程 ID ?致。如果?致,繼續(xù)下?步的判斷,如果不?致,跳轉(zhuǎn)到步驟4;
(3)判斷是否需要重偏向。如果不?的話,直接獲得偏向鎖;
(4)利? CAS 算法將對象的 Mark Word 進(jìn)?更改,使線程 ID 部分換成本線程 ID。如果更換成功,則重偏向完成,獲得偏向鎖。如果失敗,則說明有多線程競爭,升級為輕量級鎖。
12、簡述輕量級鎖。
輕量級鎖是為了在沒有競爭的前提下減少重量級鎖出現(xiàn)并導(dǎo)致的性能消耗。
其申請流程為:
(1)如果同步對象沒有被鎖定,虛擬機(jī)將在當(dāng)前線程的棧幀中建??個(gè)鎖記錄空間,存儲鎖對象?前 MarkWord 的拷?;
(2)虛擬機(jī)使? CAS 嘗試把對象的 Mark Word 更新為指向鎖記錄的指針;
(3)如果更新成功即代表該線程擁有了鎖,鎖標(biāo)志位將轉(zhuǎn)變?yōu)?00,表示處于輕量級鎖定狀態(tài);
(4)如果更新失敗就意味著?少存在?條線程與當(dāng)前線程競爭。虛擬機(jī)檢查對象的 Mark Word 是否指向當(dāng)前線程的棧幀;
(5)如果指向當(dāng)前線程的棧幀,說明當(dāng)前線程已經(jīng)擁有了鎖,直接進(jìn)?同步塊繼續(xù)執(zhí)?;
(6)如果不是則說明鎖對象已經(jīng)被其他線程搶占;
(7)如果出現(xiàn)兩條以上線程爭?同?個(gè)鎖,輕量級鎖就不再有效,將膨脹為重量級鎖,鎖標(biāo)志狀態(tài)變?yōu)?0,此時(shí)Mark Word 存儲的就是指向重量級鎖的指針,后?等待鎖的線程也必須阻塞。
13、簡述鎖優(yōu)化策略。
即?適應(yīng)?旋、鎖消除、鎖粗化、鎖升級等策略。
14、簡述Java的?旋鎖。
線程獲取鎖失敗后,可以采?這樣的策略,可以不放棄 CPU ,不停的重試內(nèi)重試,這種操作也稱為?旋鎖。
15、簡述?適應(yīng)?旋鎖。
?適應(yīng)?旋鎖?旋次數(shù)不再?為設(shè)定,通常由前?次在同?個(gè)鎖上的?旋時(shí)間及鎖的擁有者的狀態(tài)決定。
16、簡述鎖粗化。
鎖粗化的思想就是擴(kuò)?加鎖范圍,避免反復(fù)的加鎖和解鎖。
17、簡述鎖消除。
鎖消除是?種更為徹底的優(yōu)化,在編譯時(shí),Java編譯器對運(yùn)?上下?進(jìn)?掃描,去除不可能存在共享資源競爭的鎖。
18、簡述Lock與ReentrantLock。
Lock接?是 Java并發(fā)包的頂層接?。
可重?鎖 ReentrantLock 是 Lock 最常?的實(shí)現(xiàn),與 synchronized ?樣可重?。ReentrantLock 在默認(rèn)情況下是?公平的,可以通過構(gòu)造?法指定公平鎖。?旦使?了公平鎖,性能會(huì)下降。
19、簡述AQS。
AQS(AbstractQuenedSynchronizer)抽象的隊(duì)列式同步器。AQS是將每?條請求共享資源的線程封裝成?個(gè)鎖隊(duì)列的?個(gè)結(jié)點(diǎn)(Node),來實(shí)現(xiàn)鎖的分配。AQS是?來構(gòu)建鎖或其他同步組件的基礎(chǔ)框架,它使??個(gè) volatile int state 變量作為共享資源,如果線程獲取資源失敗,則進(jìn)?同步隊(duì)列等待;如果獲取成功就執(zhí)?臨界區(qū)代碼,釋放資源時(shí)會(huì)通知同步隊(duì)列中的等待線程。
?類通過繼承同步器并實(shí)現(xiàn)它的抽象?法getState、setState 和 compareAndSetState對同步狀態(tài)進(jìn)?更改。
AQS獲取獨(dú)占鎖/釋放獨(dú)占鎖原理:
(1)獲?。╝cquire):
調(diào)? tryAcquire ?法安全地獲取線程同步狀態(tài),獲取失敗的線程會(huì)被構(gòu)造同步節(jié)點(diǎn)并通過 addWaiter?法加?到同步隊(duì)列的尾部,在隊(duì)列中?旋;
調(diào)? acquireQueued ?法使得該節(jié)點(diǎn)以死循環(huán)的?式獲取同步狀態(tài),如果獲取不到則阻塞。
(2)釋放(release):
調(diào)? tryRelease ?法釋放同步狀態(tài);
調(diào)? unparkSuccessor ?法喚醒頭節(jié)點(diǎn)的后繼節(jié)點(diǎn),使后繼節(jié)點(diǎn)重新嘗試獲取同步狀態(tài)。
AQS獲取共享鎖/釋放共享鎖原理:
獲取鎖(acquireShared)
調(diào)? tryAcquireShared ?法嘗試獲取同步狀態(tài),返回值不?于 0 表示能獲取同步狀態(tài)。
釋放(releaseShared),并喚醒后續(xù)處于等待狀態(tài)的節(jié)點(diǎn)。文章來源:http://www.zghlxwxcb.cn/news/detail-406525.html
20、死鎖與活鎖的區(qū)別,死鎖與饑餓的區(qū)別?
死鎖:是指兩個(gè)或兩個(gè)以上的進(jìn)程(或線程)在執(zhí)行過程中,因爭奪資源而造成
的一種互相等待的現(xiàn)象,若無外力作用,它們都將無法推進(jìn)下去。
產(chǎn)生死鎖的必要條件:
(1)互斥條件:所謂互斥就是進(jìn)程在某一時(shí)間內(nèi)獨(dú)占資源。
(2)請求與保持條件:一個(gè)進(jìn)程因請求資源而阻塞時(shí),對已獲得的資源保持不放。
(3)不剝奪條件:進(jìn)程已獲得資源,在末使用完之前,不能強(qiáng)行剝奪。
(4)循環(huán)等待條件:若干進(jìn)程之間形成一種頭尾相接的循環(huán)等待資源關(guān)系。
活鎖:任務(wù)或者執(zhí)行者沒有被阻塞,由于某些條件沒有滿足,導(dǎo)致一直重復(fù)嘗試,
失敗,嘗試,失敗。
活鎖和死鎖的區(qū)別在于,處于活鎖的實(shí)體是在不斷的改變狀態(tài),所謂的“活”, 而
處于死鎖的實(shí)體表現(xiàn)為等待;活鎖有可能自行解開,死鎖則不能。
饑餓:一個(gè)或者多個(gè)線程因?yàn)榉N種原因無法獲得所需要的資源,導(dǎo)致一直無法執(zhí)
行的狀態(tài)。
Java 中導(dǎo)致饑餓的原因:
(1)高優(yōu)先級線程吞噬所有的低優(yōu)先級線程的 CPU 時(shí)間。
(2)線程被永久堵塞在一個(gè)等待進(jìn)入同步塊的狀態(tài),因?yàn)槠渌€程總是能在它之前
持續(xù)地對該同步塊進(jìn)行訪問。
(3)線程在等待一個(gè)本身也處于永久等待完成的對象(比如調(diào)用這個(gè)對象的 wait 方
法),因?yàn)槠渌€程總是被持續(xù)地獲得喚醒。文章來源地址http://www.zghlxwxcb.cn/news/detail-406525.html
到了這里,關(guān)于2023Java高頻必背并發(fā)編程面試題02的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!