最全多线程经典面试题和答案
Java實(shí)現(xiàn)線程有哪幾種方式?
1、繼承Thread類(lèi)實(shí)現(xiàn)多線程
2、實(shí)現(xiàn)Runnable接口方式實(shí)現(xiàn)多線程
3、使用ExecutorService、Callable、Future實(shí)現(xiàn)有返回結(jié)果的多線程
多線程同步有哪幾種方法?
Synchronized關(guān)鍵字,Lock鎖實(shí)現(xiàn),分布式鎖等。
Runnable和Thread用哪個(gè)好?
Java不支持類(lèi)的多重繼承,但允許你實(shí)現(xiàn)多個(gè)接口。所以如果你要繼承其他類(lèi),也為了減少類(lèi)之間的耦合性,Runnable會(huì)更好。
Java中notify和notifyAll有什么區(qū)別?
notify()方法不能喚醒某個(gè)具體的線程,所以只有一個(gè)線程在等待的時(shí)候它才有用武之地。而notifyAll()喚醒所有線程并允許他們爭(zhēng)奪鎖確保了至少有一個(gè)線程能繼續(xù)運(yùn)行。
為什么wait/notify/notifyAll這些方法不在thread類(lèi)里面?
這是個(gè)設(shè)計(jì)相關(guān)的問(wèn)題,它考察的是面試者對(duì)現(xiàn)有系統(tǒng)和一些普遍存在但看起來(lái)不合理的事物的看法。回答這些問(wèn)題的時(shí)候,你要說(shuō)明為什么把這些方法放在Object類(lèi)里是有意義的,還有不把它放在Thread類(lèi)里的原因。一個(gè)很明顯的原因是JAVA提供的鎖是對(duì)象級(jí)的而不是線程級(jí)的,每個(gè)對(duì)象都有鎖,通過(guò)線程獲得。如果線程需要等待某些鎖那么調(diào)用對(duì)象中的wait()方法就有意義了。如果wait()方法定義在Thread類(lèi)中,線程正在等待的是哪個(gè)鎖就不明顯了。簡(jiǎn)單的說(shuō),由于wait,notify和notifyAll都是鎖級(jí)別的操作,所以把他們定義在Object類(lèi)中因?yàn)殒i屬于對(duì)象。
為什么wait和notify方法要在同步塊中調(diào)用?
主要是因?yàn)镴ava API強(qiáng)制要求這樣做,如果你不這么做,你的代碼會(huì)拋出IllegalMonitorStateException異常。還有一個(gè)原因是為了避免wait和notify之間產(chǎn)生競(jìng)態(tài)條件。
什么是死鎖?如何避免死鎖?
死鎖就是兩個(gè)線程相互等待對(duì)方釋放對(duì)象鎖。
啟動(dòng)線程方法start()和run()有什么區(qū)別?
只有調(diào)用了start()方法,才會(huì)表現(xiàn)出多線程的特性,不同線程的run()方法里面的代碼交替執(zhí)行。如果只是調(diào)用run()方法,那么代碼還是同步執(zhí)行的,必須等待一個(gè)線程的run()方法里面的代碼全部執(zhí)行完畢之后,另外一個(gè)線程才可以執(zhí)行其run()方法里面的代碼。
多線程之間如何進(jìn)行通信?
wait/notify
什么是線程池?
很簡(jiǎn)單,簡(jiǎn)單看名字就知道是裝有線程的池子,我們可以把要執(zhí)行的多線程交給線程池來(lái)處理,和連接池的概念一樣,通過(guò)維護(hù)一定數(shù)量的線程池來(lái)達(dá)到多個(gè)線程的復(fù)用。
線程池的好處
我們知道不用線程池的話(huà),每個(gè)線程都要通過(guò)new Thread(xxRunnable).start()的方式來(lái)創(chuàng)建并運(yùn)行一個(gè)線程,線程少的話(huà)這不會(huì)是問(wèn)題,而真實(shí)環(huán)境可能會(huì)開(kāi)啟多個(gè)線程讓系統(tǒng)和程序達(dá)到最佳效率,當(dāng)線程數(shù)達(dá)到一定數(shù)量就會(huì)耗盡系統(tǒng)的CPU和內(nèi)存資源,也會(huì)造成GC頻繁收集和停頓,因?yàn)槊看蝿?chuàng)建和銷(xiāo)毀一個(gè)線程都是要消耗系統(tǒng)資源的,如果為每個(gè)任務(wù)都創(chuàng)建線程這無(wú)疑是一個(gè)很大的性能瓶頸。所以,線程池中的線程復(fù)用極大節(jié)省了系統(tǒng)資源,當(dāng)線程一段時(shí)間不再有任務(wù)處理時(shí)它也會(huì)自動(dòng)銷(xiāo)毀,而不會(huì)長(zhǎng)駐內(nèi)存。
什么是活鎖、饑餓、無(wú)鎖、死鎖?
死鎖、活鎖、饑餓是關(guān)于多線程是否活躍出現(xiàn)的運(yùn)行阻塞障礙問(wèn)題,如果線程出現(xiàn)了這三種情況,即線程不再活躍,不能再正常地執(zhí)行下去了。
死鎖
死鎖是多線程中最差的一種情況,多個(gè)線程相互占用對(duì)方的資源的鎖,而又相互等對(duì)方釋放鎖,此時(shí)若無(wú)外力干預(yù),這些線程則一直處理阻塞的假死狀態(tài),形成死鎖。
舉個(gè)例子,A同學(xué)搶了B同學(xué)的鋼筆,B同學(xué)搶了A同學(xué)的書(shū),兩個(gè)人都相互占用對(duì)方的東西,都在讓對(duì)方先還給自己自己再還,這樣一直爭(zhēng)執(zhí)下去等待對(duì)方還而又得不到解決,老師知道此事后就讓他們相互還給對(duì)方,這樣在外力的干預(yù)下他們才解決,當(dāng)然這只是個(gè)例子沒(méi)有老師他們也能很好解決,計(jì)算機(jī)不像人如果發(fā)現(xiàn)這種情況沒(méi)有外力干預(yù)還是會(huì)一直阻塞下去的。
活鎖
活鎖這個(gè)概念大家應(yīng)該很少有人聽(tīng)說(shuō)或理解它的概念,而在多線程中這確實(shí)存在。活鎖恰恰與死鎖相反,死鎖是大家都拿不到資源都占用著對(duì)方的資源,而活鎖是拿到資源卻又相互釋放不執(zhí)行。當(dāng)多線程中出現(xiàn)了相互謙讓,都主動(dòng)將資源釋放給別的線程使用,這樣這個(gè)資源在多個(gè)線程之間跳動(dòng)而又得不到執(zhí)行,這就是活鎖。
饑餓
我們知道多線程執(zhí)行中有線程優(yōu)先級(jí)這個(gè)東西,優(yōu)先級(jí)高的線程能夠插隊(duì)并優(yōu)先執(zhí)行,這樣如果優(yōu)先級(jí)高的線程一直搶占優(yōu)先級(jí)低線程的資源,導(dǎo)致低優(yōu)先級(jí)線程無(wú)法得到執(zhí)行,這就是饑餓。當(dāng)然還有一種饑餓的情況,一個(gè)線程一直占著一個(gè)資源不放而導(dǎo)致其他線程得不到執(zhí)行,與死鎖不同的是饑餓在以后一段時(shí)間內(nèi)還是能夠得到執(zhí)行的,如那個(gè)占用資源的線程結(jié)束了并釋放了資源。
無(wú)鎖
無(wú)鎖,即沒(méi)有對(duì)資源進(jìn)行鎖定,即所有的線程都能訪問(wèn)并修改同一個(gè)資源,但同時(shí)只有一個(gè)線程能修改成功。無(wú)鎖典型的特點(diǎn)就是一個(gè)修改操作在一個(gè)循環(huán)內(nèi)進(jìn)行,線程會(huì)不斷的嘗試修改共享資源,如果沒(méi)有沖突就修改成功并退出否則就會(huì)繼續(xù)下一次循環(huán)嘗試。所以,如果有多個(gè)線程修改同一個(gè)值必定會(huì)有一個(gè)線程能修改成功,而其他修改失敗的線程會(huì)不斷重試直到修改成功。之前的文章我介紹過(guò)JDK的CAS原理及應(yīng)用即是無(wú)鎖的實(shí)現(xiàn)。
可以看出,無(wú)鎖是一種非常良好的設(shè)計(jì),它不會(huì)出現(xiàn)線程出現(xiàn)的跳躍性問(wèn)題,鎖使用不當(dāng)肯定會(huì)出現(xiàn)系統(tǒng)性能問(wèn)題,雖然無(wú)鎖無(wú)法全面代替有鎖,但無(wú)鎖在某些場(chǎng)合下是非常高效的。
Synchronized有哪幾種用法?
鎖類(lèi)、鎖方法、鎖代碼塊。
Fork/Join框架是干什么的?
大任務(wù)自動(dòng)分散小任務(wù),并發(fā)執(zhí)行,合并小任務(wù)結(jié)果。
Java中用到了什么線程調(diào)度算法?
搶占式。一個(gè)線程用完CPU之后,操作系統(tǒng)會(huì)根據(jù)線程優(yōu)先級(jí)、線程饑餓情況等數(shù)據(jù)算出一個(gè)總的優(yōu)先級(jí)并分配下一個(gè)時(shí)間片給某個(gè)線程執(zhí)行。
你可能也喜歡:
總結(jié)
以上是生活随笔為你收集整理的最全多线程经典面试题和答案的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 在传统Spring应用中使用spring
- 下一篇: 配送交付时间轻量级预估实践