1. 什么叫死鎖万细?
- 死鎖就是多個線程對自己持有的資源不釋放,同時(shí)又去申請對方持有的資源纸泄,形成循環(huán)等待赖钞。
2. 什么是樂觀鎖?什么是悲觀鎖聘裁?
- 樂觀鎖就是很樂觀雪营,認(rèn)為別人不會修改它的數(shù)據(jù),不會上鎖衡便,每次操作前會判斷別人有沒有改過它的數(shù)據(jù)献起,CAS 就屬于樂觀鎖;
- 悲觀鎖就是很悲觀镣陕,認(rèn)為別人一定會改它的數(shù)據(jù)谴餐,每次操作都會上鎖,Synchronized 和 ReentrantLock 都是悲觀鎖呆抑。
3. 什么是自旋鎖岂嗓?適應(yīng)性自旋鎖呢
- 阻塞和喚醒線程需要系統(tǒng)切換CPU狀態(tài),這個開銷是比較大的鹊碍。如果一段代碼的執(zhí)行時(shí)間很短厌殉,剛阻塞線程,代碼就執(zhí)行完了侈咕,接著又喚醒線程公罕,這種方式效率不高,倒不如讓線程不阻塞耀销,等代碼執(zhí)行完楼眷。那么就可以讓線程自旋,不必阻塞树姨,等前面線程執(zhí)行完它就可以獲取資源,這就是自旋鎖桥状。實(shí)現(xiàn)原理就是 CAS帽揪。適應(yīng)性自旋鎖就是虛擬機(jī)會判斷自旋獲取到鎖的概率大不大,如果大辅斟,就自旋转晰,如果不大,就阻塞線程。
4. 什么是共享鎖查邢?什么是獨(dú)占鎖蔗崎?
- 共享鎖就是可以同時(shí)被多個線程持有,一個資源被加上共享鎖后扰藕,那么其他線程也只能對其加共享鎖缓苛。獲得共享鎖的線程只能讀數(shù)據(jù),不能寫數(shù)據(jù)邓深。ReentrantReadWriteLock 的讀鎖是共享鎖未桥,寫鎖是獨(dú)占鎖。
- 獨(dú)占鎖又叫排他鎖芥备,同一時(shí)刻只能被一個線程持有冬耿,獲取到鎖的線程可以對資源進(jìn)行讀寫操作。Synchronized 和 ReentrantLock 就是獨(dú)占鎖萌壳。
5. 什么是可重入鎖亦镶?
- 可重入鎖又叫遞歸鎖,就是一個線程獲取到鎖后袱瓮,就可以進(jìn)入它同步著的所有代碼缤骨,即使內(nèi)層函數(shù)也被鎖住,也無需重新獲取鎖懂讯,Synchronized 和 ReentrantLock 都是可重入鎖荷憋。
6. 可重入鎖的原理是什么?
- 有個 state 變量褐望,線程獲取到鎖就加 1勒庄,釋放鎖就減 1,線程獲取鎖的時(shí)候會判斷 state 是不是 0瘫里,是 0 就讓線程獲取鎖实蔽,state 加 1,如果不是 0谨读,但是當(dāng)前持有鎖的線程等于當(dāng)前線程局装,state 也會加 1。
7. 公平鎖和非公平鎖有什么區(qū)別劳殖?
- 公平鎖就是按照申請鎖的順序去獲取鎖铐尚,先來后到,而非公平鎖并不會按照順序哆姻,在高并發(fā)的情況下可能出現(xiàn)優(yōu)先級反轉(zhuǎn)和饑餓現(xiàn)象宣增,就是優(yōu)先級高的反而后獲取到鎖,或者某些線程一直沒有獲取到鎖矛缨。Synchronized 就是非公平鎖爹脾,ReentrantLock 可以默認(rèn)非公平鎖帖旨,可以通過參數(shù)設(shè)置為公平鎖。
8. CAS 是什么灵妨,會有什么問題嗎解阅?怎么解決?
- CAS 就是比較并交換泌霍,它有三個操作數(shù)货抄,內(nèi)存值,期望值烹吵,更新值碉熄。當(dāng)且僅當(dāng)內(nèi)存值等于期望值時(shí),才會把內(nèi)存值修改為更新值肋拔,它以自旋的方式同步線程锈津,減少了線程切換的開銷。它會出現(xiàn) ABA 問題凉蜂,就是線程 1 將共享變量 A 改為 B琼梆,再改為 A,線程 2 去判斷的時(shí)候窿吩,以為沒有別的線程改過茎杂,解決辦法是可以每次操作都加個版本號。還有個問題就是 CAS 只能保證一個變量的原子操作纫雁,可以用原子引用來解決煌往。如果 CAS 長時(shí)間不成功,就會一直自旋轧邪,占用大量的 CPU刽脖,可以加次數(shù)限制。CAS 底層是通過內(nèi)存偏移量來獲取內(nèi)存值的忌愚。
9. 說說你對 AQS 的理解曲管?
- AQS,又叫抽象隊(duì)列同步器硕糊,它是 JUC 的基石院水。JUC 包下的鎖、并發(fā)工具简十,都有一些相似的代碼檬某,然后把這些代碼抽取出來,就是 AQS螟蝙,也即 JUC 包下的都是基于 AQS 去構(gòu)建的恢恼。
10. AQS 是怎么協(xié)調(diào)工作的?
- AQS 底層是通過 LockSupport 實(shí)現(xiàn)的胶逢。LockSupport 是等待喚醒的另一種實(shí)現(xiàn)方式厅瞎,它使用一個通行證的概念。每個線程都有一個通行證初坠,通行證的數(shù)量只可能為 0 或者 1和簸,默認(rèn)是 0。調(diào)用 unpark 方法的時(shí)候會發(fā)放一個通行證碟刺,線程就會被喚醒锁保;調(diào)用 park 方法的時(shí)候,就會消耗一個通行證半沽,線程就會被阻塞爽柒。park 和 unpark 方法底層是調(diào)用了 unsafe 類的 native 方法。
11. AQS 的工作原理是什么者填?
- 有個 volatile 修飾的 int 類型的 state 變量浩村,用來表示同步狀態(tài),將線程封裝成 node 節(jié)點(diǎn)占哟,通過內(nèi)置的隊(duì)列來完成資源的獲取和排隊(duì)工作心墅,用 CAS 來完成對狀態(tài)的修改。
12. 線程申請資源的時(shí)候榨乎,AQS 是怎么進(jìn)行入隊(duì)和出隊(duì)工作的怎燥?
- 入隊(duì):假如現(xiàn)在線程 A 正持有鎖,此時(shí)線程 B 想獲取鎖蜜暑,就要進(jìn)入到隊(duì)列中等待铐姚。如果隊(duì)列還是空的,首先會創(chuàng)建一個節(jié)點(diǎn)肛捍,稱為傀儡節(jié)點(diǎn)隐绵,然后把隊(duì)列的 head 指針和 tail 指針都指向它,然后把線程 B 封裝成一個節(jié)點(diǎn)篇梭,然后把這個節(jié)點(diǎn)的 prev 指向傀儡節(jié)點(diǎn)氢橙,把傀儡節(jié)點(diǎn)的 next 指向該節(jié)點(diǎn),最后把 tail 指針指向該節(jié)點(diǎn)恬偷。
- 出隊(duì):假如現(xiàn)在線程 A 釋放鎖了悍手,那么線程 B 對應(yīng)的節(jié)點(diǎn)就應(yīng)該出隊(duì)。首先會把 head 指向線程 B 對應(yīng)的節(jié)點(diǎn)袍患,然后把線程 B 對應(yīng)節(jié)點(diǎn)的線程設(shè)置為空坦康,接著把該節(jié)點(diǎn)的 prev 設(shè)置為空,把傀儡節(jié)點(diǎn)的 next 設(shè)置為空诡延,這樣一來滞欠,原先線程 B 所在的節(jié)點(diǎn)就成了新的傀儡節(jié)點(diǎn),原先的傀儡節(jié)點(diǎn)就沒有任何引用指向它肆良,就會被 GC 回收筛璧。
13. 你知道哪些并發(fā)關(guān)鍵字逸绎?
- synchronized,volatile夭谤、final棺牧。
14. 說說你對 synchronized 的理解?
- 它是一個關(guān)鍵字朗儒,是可重入的非公平鎖颊乘」芟可以修飾實(shí)例方法奕删、靜態(tài)方法和代碼塊付枫。修飾實(shí)例方法時(shí)鎖對象是實(shí)例翩瓜,修飾靜態(tài)方法時(shí)鎖對象是當(dāng)前類查牌,修飾代碼塊時(shí)鎖對象可以任意對象交惯。修飾方法時(shí)罩阵,通過 javap 命令反匯編可以看到它是通過 ACC_SYNCHRONIZED 標(biāo)識來實(shí)現(xiàn)同步的杜耙;而修飾代碼塊時(shí)是通過 monitor 對象來實(shí)現(xiàn)同步的烟勋,monitorenter 指向鎖開始的地方识啦,monitorexist 指向鎖退出的地方,并且有兩個 monitorexist神妹,是為了防止程序異常導(dǎo)致鎖未釋放颓哮。
15. 鎖狀態(tài)有哪些?
無鎖鸵荠、偏向鎖冕茅、輕量級鎖、重量級鎖蛹找。
- 無鎖就是不阻塞線程姨伤,在循環(huán)內(nèi)不斷地嘗試,CAS 便是無鎖的實(shí)現(xiàn)庸疾;
- 偏向鎖就是在鎖對象頭里會保存當(dāng)前持鎖的線程 ID乍楚,如果申請資源的線程 ID 等于對象頭里保存的線程 ID,那就直接讓線程獲取鎖届慈;
- 輕量級鎖就是當(dāng)鎖是偏向鎖時(shí)別的線程進(jìn)來請求資源了徒溪,那就會自旋一定次數(shù)去嘗試獲取鎖,自旋一定次數(shù)沒獲取到金顿,又或者是一個線程持有鎖臊泌,一個在自旋,第三個線程進(jìn)來了揍拆,都會升級為重量級鎖渠概;
- 重量級鎖就是等待鎖的線程都會阻塞。