Java實(shí)現(xiàn)線程有哪幾種方式浪讳?
1、繼承Thread類實(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不支持類的多重繼承,但允許你實(shí)現(xiàn)多個(gè)接口馋评。所以如果你要繼承其他類放接,也為了減少類之間的耦合性,Runnable會(huì)更好留特。
Java中notify和notifyAll有什么區(qū)別纠脾?
notify()方法不能喚醒某個(gè)具體的線程,所以只有一個(gè)線程在等待的時(shí)候它才有用武之地蜕青。而notifyAll()喚醒所有線程并允許他們爭奪鎖確保了至少有一個(gè)線程能繼續(xù)運(yùn)行苟蹈。
為什么wait/notify/notifyAll這些方法不在thread類里面?
這是個(gè)設(shè)計(jì)相關(guān)的問題右核,它考察的是面試者對現(xiàn)有系統(tǒng)和一些普遍存在但看起來不合理的事物的看法慧脱。回答這些問題的時(shí)候贺喝,你要說明為什么把這些方法放在Object類里是有意義的菱鸥,還有不把它放在Thread類里的原因。一個(gè)很明顯的原因是JAVA提供的鎖是對象級(jí)的而不是線程級(jí)的躏鱼,每個(gè)對象都有鎖氮采,通過線程獲得。如果線程需要等待某些鎖那么調(diào)用對象中的wait()方法就有意義了染苛。如果wait()方法定義在Thread類中鹊漠,線程正在等待的是哪個(gè)鎖就不明顯了。簡單的說,由于wait躯概,notify和notifyAll都是鎖級(jí)別的操作登钥,所以把他們定義在Object類中因?yàn)殒i屬于對象。
為什么wait和notify方法要在同步塊中調(diào)用楞陷?
主要是因?yàn)镴ava API強(qiáng)制要求這樣做怔鳖,如果你不這么做,你的代碼會(huì)拋出IllegalMonitorStateException異常固蛾。還有一個(gè)原因是為了避免wait和notify之間產(chǎn)生競態(tài)條件结执。
什么是死鎖?如何避免死鎖艾凯?
死鎖就是兩個(gè)線程相互等待對方釋放對象鎖献幔。
啟動(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
什么是線程池?
很簡單览效,簡單看名字就知道是裝有線程的池子却舀,我們可以把要執(zhí)行的多線程交給線程池來處理,和連接池的概念一樣锤灿,通過維護(hù)一定數(shù)量的線程池來達(dá)到多個(gè)線程的復(fù)用挽拔。
線程池的好處
我們知道不用線程池的話,每個(gè)線程都要通過new Thread(xxRunnable).start()的方式來創(chuàng)建并運(yùn)行一個(gè)線程但校,線程少的話這不會(huì)是問題螃诅,而真實(shí)環(huán)境可能會(huì)開啟多個(gè)線程讓系統(tǒng)和程序達(dá)到最佳效率,當(dāng)線程數(shù)達(dá)到一定數(shù)量就會(huì)耗盡系統(tǒng)的CPU和內(nèi)存資源状囱,也會(huì)造成GC頻繁收集和停頓州刽,因?yàn)槊看蝿?chuàng)建和銷毀一個(gè)線程都是要消耗系統(tǒng)資源的,如果為每個(gè)任務(wù)都創(chuàng)建線程這無疑是一個(gè)很大的性能瓶頸浪箭。所以穗椅,線程池中的線程復(fù)用極大節(jié)省了系統(tǒng)資源,當(dāng)線程一段時(shí)間不再有任務(wù)處理時(shí)它也會(huì)自動(dòng)銷毀奶栖,而不會(huì)長駐內(nèi)存匹表。
什么是活鎖门坷、饑餓、無鎖袍镀、死鎖默蚌?
死鎖、活鎖苇羡、饑餓是關(guān)于多線程是否活躍出現(xiàn)的運(yùn)行阻塞障礙問題绸吸,如果線程出現(xiàn)了這三種情況,即線程不再活躍设江,不能再正常地執(zhí)行下去了锦茁。
死鎖
死鎖是多線程中最差的一種情況,多個(gè)線程相互占用對方的資源的鎖叉存,而又相互等對方釋放鎖码俩,此時(shí)若無外力干預(yù),這些線程則一直處理阻塞的假死狀態(tài)歼捏,形成死鎖稿存。
舉個(gè)例子,A同學(xué)搶了B同學(xué)的鋼筆瞳秽,B同學(xué)搶了A同學(xué)的書瓣履,兩個(gè)人都相互占用對方的東西,都在讓對方先還給自己自己再還练俐,這樣一直爭執(zhí)下去等待對方還而又得不到解決袖迎,老師知道此事后就讓他們相互還給對方,這樣在外力的干預(yù)下他們才解決痰洒,當(dāng)然這只是個(gè)例子沒有老師他們也能很好解決,計(jì)算機(jī)不像人如果發(fā)現(xiàn)這種情況沒有外力干預(yù)還是會(huì)一直阻塞下去的浴韭。
活鎖
活鎖這個(gè)概念大家應(yīng)該很少有人聽說或理解它的概念丘喻,而在多線程中這確實(shí)存在∧罹保活鎖恰恰與死鎖相反泉粉,死鎖是大家都拿不到資源都占用著對方的資源,而活鎖是拿到資源卻又相互釋放不執(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í)線程無法得到執(zhí)行哩至,這就是饑餓。當(dāng)然還有一種饑餓的情況蜜自,一個(gè)線程一直占著一個(gè)資源不放而導(dǎo)致其他線程得不到執(zhí)行菩貌,與死鎖不同的是饑餓在以后一段時(shí)間內(nèi)還是能夠得到執(zhí)行的,如那個(gè)占用資源的線程結(jié)束了并釋放了資源重荠。
無鎖
無鎖箭阶,即沒有對資源進(jìn)行鎖定,即所有的線程都能訪問并修改同一個(gè)資源戈鲁,但同時(shí)只有一個(gè)線程能修改成功仇参。無鎖典型的特點(diǎn)就是一個(gè)修改操作在一個(gè)循環(huán)內(nèi)進(jìn)行,線程會(huì)不斷的嘗試修改共享資源荞彼,如果沒有沖突就修改成功并退出否則就會(huì)繼續(xù)下一次循環(huán)嘗試冈敛。所以,如果有多個(gè)線程修改同一個(gè)值必定會(huì)有一個(gè)線程能修改成功鸣皂,而其他修改失敗的線程會(huì)不斷重試直到修改成功抓谴。之前的文章我介紹過JDK的CAS原理及應(yīng)用即是無鎖的實(shí)現(xiàn)。
可以看出寞缝,無鎖是一種非常良好的設(shè)計(jì)癌压,它不會(huì)出現(xiàn)線程出現(xiàn)的跳躍性問題,鎖使用不當(dāng)肯定會(huì)出現(xiàn)系統(tǒng)性能問題荆陆,雖然無鎖無法全面代替有鎖滩届,但無鎖在某些場合下是非常高效的。
Synchronized有哪幾種用法被啼?
鎖類帜消、鎖方法、鎖代碼塊浓体。
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í)行碍讯。