2021Java面試題合集-Java并發(fā)編程最全面試題123道_第1頁(yè)
2021Java面試題合集-Java并發(fā)編程最全面試題123道_第2頁(yè)
2021Java面試題合集-Java并發(fā)編程最全面試題123道_第3頁(yè)
2021Java面試題合集-Java并發(fā)編程最全面試題123道_第4頁(yè)
2021Java面試題合集-Java并發(fā)編程最全面試題123道_第5頁(yè)
已閱讀5頁(yè),還剩35頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說(shuō)明:本文檔由用戶(hù)提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

基礎(chǔ)知識(shí)

1.為什么要使用并發(fā)編程

?提升多核CPU的利用率:一般來(lái)說(shuō)一臺(tái)主機(jī)上的會(huì)有多個(gè)CPU核心,我們可以創(chuàng)建多個(gè)線程,理論

上講操作系統(tǒng)可以將多個(gè)線程分配給不同的CPU去執(zhí)行,每個(gè)CPU執(zhí)行一個(gè)線程,這樣就提高了

CPU的使用效率,如果使用單線程就只能有一個(gè)CPU核心被使用。

?比如當(dāng)我們?cè)诰W(wǎng)上購(gòu)物時(shí),為了提升響應(yīng)速度,需要拆分,減庫(kù)存,生成訂單等等這些操作,就可

以進(jìn)行拆分利用多線程的技術(shù)完成。面對(duì)復(fù)雜業(yè)務(wù)模型,并行程序會(huì)比串行程序更適應(yīng)業(yè)務(wù)需求,

而并發(fā)編程更能吻合這種業(yè)務(wù)拆分。

?簡(jiǎn)單來(lái)說(shuō)就是:

。充分利用多核CPU的計(jì)算能力;

。方便進(jìn)行業(yè)務(wù)拆分,提升應(yīng)用性能

2.多線程應(yīng)用場(chǎng)景

?例如:迅雷多線程下載、數(shù)據(jù)庫(kù)連接池、分批發(fā)送短信等。

3.并發(fā)編程有什么缺點(diǎn)

?并發(fā)編程的目的就是為了能提高程序的執(zhí)行效率,提高程序運(yùn)行速度,但是并發(fā)編程并不總是能提

高程序運(yùn)行速度的,而且并發(fā)編程可能會(huì)遇到很多問(wèn)題,比如:內(nèi)存泄漏、上下文切換、線程安

全、死鎖等問(wèn)題。

4.并發(fā)編程三介必要因素是什么?

?原子性:原子,即一個(gè)不可再被分割的顆粒。原子性指的是一個(gè)或多個(gè)操作要么全部執(zhí)行成功要么

全部執(zhí)行失敗.

?可見(jiàn)性:一個(gè)線程對(duì)共享變量的修改,另一線程能夠立刻看到。(synchronized,volatile)

?有序性:程序執(zhí)行的順序按照代碼的先后順序執(zhí)行。(處理器可能會(huì)對(duì)指令進(jìn)行重排序)

5.Java程序中怎么保證多線程的運(yùn)行安全?

?出現(xiàn)線程安全問(wèn)題的原因一般都是三個(gè)原因:

。線程切換帶來(lái)的原子性問(wèn)題解決辦法:使用多線程之間同步synchronized或使用鎖(lock)。

。緩存導(dǎo)致的可見(jiàn)性問(wèn)題解決辦法:synchronized,volatile.LOCK,可以解決可見(jiàn)性問(wèn)題

。編譯優(yōu)化帶來(lái)的有序性問(wèn)題解決辦法:H叩pens-Before規(guī)則可以解決有序性問(wèn)題

6.并行和并發(fā)有什么區(qū)別?

?并發(fā):多個(gè)任務(wù)在同一個(gè)CPU核上,按細(xì)分的時(shí)間片輪流(交替)執(zhí)行,從邏輯上來(lái)看那些任務(wù)是

同時(shí)執(zhí)行。

?并行:?jiǎn)挝粫r(shí)間內(nèi),多個(gè)處理器或多核處理器同時(shí)處理多個(gè)任務(wù),是真正意義上的“同時(shí)進(jìn)行"。

?串行:有n個(gè)任務(wù),由一個(gè)線程按順序執(zhí)行。由于任務(wù)、方法都在一個(gè)線程執(zhí)行所以不存在線程不

安全情況,也就不存在臨界區(qū)的問(wèn)題。

做一個(gè)形象的比喻:

?并發(fā)=倆個(gè)人用一臺(tái)電腦。

?并行=倆個(gè)人分配了倆臺(tái)電腦。

?串行=倆個(gè)人排隊(duì)使用一臺(tái)電腦。

7.什么是多線程

?多線程:多線程是指程序中包含多個(gè)執(zhí)行流,即在一個(gè)程序中可以同時(shí)運(yùn)行多個(gè)不同的線程來(lái)執(zhí)行

不同的任務(wù)。

8.多線程的好處

?可以提高CPU的利用率。在多線程程序中,一個(gè)線程必須等待的時(shí)候,CPU可以運(yùn)行其它的線程

而不是等待,這樣就大大提高了程序的效率。也就是說(shuō)允許單個(gè)程序創(chuàng)建多個(gè)并行執(zhí)行的線程來(lái)完

成各自的任務(wù).

9.多線程的劣勢(shì):

?線程也是程序,所以線程需要占用內(nèi)存,線程越多占用內(nèi)存也越多;

?多線程需要協(xié)調(diào)和管理,所以需要CPU時(shí)間跟蹤線程;

?線程之間對(duì)共享資源的訪問(wèn)會(huì)相互影響,必須解決競(jìng)用共享資源的問(wèn)題。

10.線程和進(jìn)程區(qū)別

?什么是線程和進(jìn)程?

。進(jìn)程

一個(gè)在內(nèi)存中運(yùn)行的應(yīng)用程序。每個(gè)正在系統(tǒng)上運(yùn)行的程序都是一個(gè)進(jìn)程

?線程

進(jìn)程中的一個(gè)執(zhí)行任務(wù)(控制單元),它負(fù)責(zé)在程序里獨(dú)立執(zhí)行。

一個(gè)進(jìn)程至少有一個(gè)線程,?個(gè)進(jìn)程可以運(yùn)行多個(gè)線程,多個(gè)線程可共享數(shù)據(jù)。

?進(jìn)程與線程的區(qū)別

。根本區(qū)別:進(jìn)程是操作系統(tǒng)資源分配的基本單位,而線程是處理器任務(wù)調(diào)度和執(zhí)行的基本單

。資源開(kāi)銷(xiāo):每個(gè)進(jìn)程都有獨(dú)立的代碼和數(shù)據(jù)空間(程序上下文),程序之間的切換會(huì)有較大

的開(kāi)銷(xiāo);線程可以看做輕量級(jí)的進(jìn)程,同一類(lèi)線程共享代碼和數(shù)據(jù)空間,每個(gè)線程都有自己

獨(dú)立的運(yùn)行榭口程序計(jì)數(shù)器(PC),線程之間切換的開(kāi)銷(xiāo)小。

。包含關(guān)系:如果一個(gè)進(jìn)程內(nèi)有多個(gè)線程,則執(zhí)行過(guò)程不是一條線的,而是多條線(線程)共

同完成的;線程是進(jìn)程的一部分,所以線程也被稱(chēng)為輕權(quán)進(jìn)程或者輕量級(jí)進(jìn)程。

。內(nèi)存分配:同一進(jìn)程的線程共享本進(jìn)程的地址空間和資源,而進(jìn)程與進(jìn)程之間的地址空間和

資源是相互獨(dú)立的

。影響關(guān)系:一個(gè)進(jìn)程崩潰后,在保護(hù)模式下不會(huì)對(duì)其他進(jìn)程產(chǎn)生影響,但是一個(gè)線程崩潰有

可能導(dǎo)致整個(gè)進(jìn)程都死掉。所以多進(jìn)程要比多線程健壯。

。執(zhí)行過(guò)程:每個(gè)獨(dú)立的進(jìn)程有程序運(yùn)行的入口、順序執(zhí)行序列和程序出口。但是線程不能獨(dú)

立執(zhí)行,必須依存在應(yīng)用程序中,由應(yīng)用程序提供多個(gè)線程執(zhí)行控制,兩者均可并發(fā)執(zhí)行

11.什么是上下文切換?

?多線程編程中一般線程的個(gè)數(shù)都大于CPU核心的個(gè)數(shù),而一個(gè)CPU核心在任意時(shí)刻只能被一個(gè)線

程使用,為了讓這些線程都能得到有效執(zhí)行,CPU采取的策略是為每個(gè)線程分配時(shí)間片并輪轉(zhuǎn)的

形式。當(dāng)一個(gè)線程的時(shí)間片用完的時(shí)候就會(huì)重新處于就緒狀態(tài)讓給其他線程使用,這個(gè)過(guò)程就屬于

一次上下文切換。

?概括來(lái)說(shuō)就是:當(dāng)前任務(wù)在執(zhí)行完CPU時(shí)間片切換到另一個(gè)任務(wù)之前會(huì)先保存自己的狀態(tài),以便

下次再切換回這個(gè)任務(wù)時(shí),可以再加載這個(gè)任務(wù)的狀態(tài)。任務(wù)從保存到再加載的過(guò)程就是一次上下

文切換。

?上下文切換通常是計(jì)算密集型的。也就是說(shuō),它需要相當(dāng)可觀的處理器時(shí)間,在每秒幾十上百次的

切換中,每次切換都需要納秒量級(jí)的時(shí)間。所以,上下文切換對(duì)系統(tǒng)來(lái)說(shuō)意味著消耗大量的CPU

時(shí)間,事實(shí)上,可能是操作系統(tǒng)中時(shí)間消耗最大的操作。

?Linux相比與其他操作系統(tǒng)(包括其他類(lèi)Unix系統(tǒng))有很多的優(yōu)點(diǎn),其中有一項(xiàng)就是,其上下文

切換和模式切換的時(shí)間消耗非常少。

12.守護(hù)線程和用戶(hù)線程有什么區(qū)別呢?

?用戶(hù)(User)線程:運(yùn)行在前臺(tái),執(zhí)行具體的任務(wù),如程序的主線程、連接網(wǎng)絡(luò)的子線程等都是用

戶(hù)線程

?守護(hù)(Daemon)線程:運(yùn)行在后臺(tái),為其他前臺(tái)線程^務(wù)。也可以說(shuō)守護(hù)線程是JVM中非守護(hù)線

程的“傭人"。一旦所有用戶(hù)線程都結(jié)束運(yùn)行,守護(hù)線程會(huì)隨JVM一起結(jié)束工作

13.如何在Windows和Linux上查找哪個(gè)線程cpu利用率最高?

?windows上面用任務(wù)管理器看,linux下可以用top這個(gè)工具看。

。找出cpu耗用厲害的進(jìn)程pid,終端執(zhí)行top命令,然后按下shift+p(shi代+m是找出消耗內(nèi)存

最高)查找出cpu利用最厲害的pid號(hào)

根據(jù)上面第一^拿到的pid號(hào),top-H-ppid。然后按下shift+p,查找出cpu利用率最厲害的

線程號(hào),比如top-H-p1328

■將獲取到的線程號(hào)轉(zhuǎn)換成16進(jìn)制,去百度轉(zhuǎn)換一下就行

。使用jstack工具將進(jìn)程信息打印輸出,jstackpid號(hào)>/tmp/t.dat,比如jstack31365>

/tmp/t.dat

■編輯/tmp/t.dat文件,查找線程號(hào)對(duì)應(yīng)的信息

或者直接使用JDK自帶的工具查看“jconsole"、“visualvm",這都是JDK自帶的,可以直接在JDK的bin目錄

下找到直接使用

14.什么是線程死鎖

?死鎖是指兩個(gè)或兩個(gè)以上的進(jìn)程(線程)在執(zhí)行過(guò)程中,由于競(jìng)爭(zhēng)資源或者由于彼此通信而造成的

一種阻塞的現(xiàn)象,若無(wú)外力作用,它們都將無(wú)法推進(jìn)下去.此時(shí)稱(chēng)系統(tǒng)處于死鎖狀態(tài)或系統(tǒng)產(chǎn)生了

死鎖,這些永遠(yuǎn)在互相等待的進(jìn)程(線程)稱(chēng)為死鎖進(jìn)程(線程)。

?多個(gè)線程同時(shí)被阻塞,它們中的一個(gè)或者全部都在等待某個(gè)資源被釋放。由于線程被無(wú)限期地阻

塞,因此程序不可能正常終止。

?如下圖所示,線程A持有資源2,線程B持有資源1,他們同時(shí)都想申請(qǐng)對(duì)方的資源,所以這兩個(gè)

線程就會(huì)互相等待而進(jìn)入死鎖狀態(tài)。

線程一線程二

15.形成死鎖的四介必要條件是什么

?互斥條件:在一段時(shí)間內(nèi)某資源只由一個(gè)進(jìn)程占用。如果此時(shí)還有其它進(jìn)程請(qǐng)求資源,就只能等

待,直至占有資源的進(jìn)程用畢釋放。

?占有且等待條件:指進(jìn)程已經(jīng)保持至少一個(gè)資源,但又提出了新的資源請(qǐng)求,而該資源已被其它進(jìn)

程占有,此時(shí)請(qǐng)求進(jìn)程阻塞,但又對(duì)自己已獲得的其它資源保持不放。

?不可搶占條件:別人已經(jīng)占有了某項(xiàng)資源,你不能因?yàn)樽约阂残枰撡Y源,就去把別人的資源搶過(guò)

來(lái)。

?循環(huán)等待條件:若干進(jìn)程之間形成一種頭尾相接的循環(huán)等待資源關(guān)系。(比如一個(gè)進(jìn)程集合,A在

等B,B在等C,C在等A)

16.如何避免線程死鎖

1.避免一個(gè)線程同時(shí)獲得多個(gè)鎖

2.避免一個(gè)線程在鎖內(nèi)同時(shí)占用多個(gè)資源,盡量保證每個(gè)鎖只占用一個(gè)資源

3.嘗試使用定時(shí)鎖,使用此1<.四1_。d<e1716。戊)來(lái)替代使用內(nèi)部鎖機(jī)制

17.創(chuàng)建線程的四種方式

?繼承Thread類(lèi);

publicclassMyThreadextendsThread{

?Override

publicvoidrun(){

System.out.println(Thread.currentThread().getName()+"run()方法正在執(zhí)

行…”);

?

?實(shí)現(xiàn)Runnable接口;

publicclassMyRunnableimplementsRunnable{

?Override

publicvoidrun(){

System.out.println(Thread.currentThread().getName()+"run()方法執(zhí)彳亍

中??.”);

)

?實(shí)現(xiàn)Callable接口;

publicclassMyCallableimplementsCal1able<lnteger>{

?Override

publicintegercall(){

System.out.println(Thread.currentThread().getName()+"call()方法執(zhí)彳亍

中…”);

return1;

)

?使用匿名內(nèi)部類(lèi)方式

publicclassCreateRunnable{

publicstaticvoidmain(String[]args){

//創(chuàng)建多線程創(chuàng)建開(kāi)始

Threadthread=newThread(newRunnable(){

publicvoidrun(){

for(inti=0;i<10;i++){

System.out.println("i:"+i);

)

)

});

thread.startf);

)

}

18.說(shuō)一下runnable和callable有什么區(qū)別

相同點(diǎn):

?都是接口

?都可以編寫(xiě)多線程程序

?都采用Thread.start。啟動(dòng)線程

主要區(qū)別:

?Runnable接口run方法無(wú)返回值;Callable接口call方法有返回值,是個(gè)泛型,和Future、

FutureTask配合可以用來(lái)獲取異步執(zhí)行的結(jié)果

?Runnable接口run方法只能拋出運(yùn)行時(shí)異常,且無(wú)法捕獲處理;Callable接口call方法允許拋出

異常,可以獲取異常信息注:Callalbe接口支持返回執(zhí)行結(jié)果,需要調(diào)用FutureTask.getO得到,

此方法會(huì)阻塞主進(jìn)程的繼續(xù)往下執(zhí)行,如果不調(diào)用不會(huì)阻塞。

19.線程的run。和start。有什么區(qū)別?

?每個(gè)線程都是通過(guò)某個(gè)特定Thread對(duì)象所對(duì)應(yīng)的方法run()來(lái)完成其操作的,run()方法稱(chēng)為線程

體。通過(guò)調(diào)用Thread類(lèi)的start。方法來(lái)啟動(dòng)一個(gè)線程。

?start。方法用于啟動(dòng)線程,run()方法用于執(zhí)行線程的運(yùn)行時(shí)代碼。run()可以重復(fù)調(diào)用,而start。

只能調(diào)用一次。

?start。方法來(lái)啟動(dòng)一個(gè)線程,真正實(shí)現(xiàn)了多線程運(yùn)行。調(diào)用start。方法無(wú)需等待run方法體代碼執(zhí)

行完畢,可以直接繼續(xù)執(zhí)行其他的代碼;此時(shí)線程是處于就緒狀態(tài),并沒(méi)有運(yùn)行。然后通過(guò)此

Thread類(lèi)調(diào)用方法run()來(lái)完成其運(yùn)行狀態(tài),run()方法運(yùn)行結(jié)束,此線程終止。然后CPU再調(diào)度

其它線程。

?run()方法是在本線程里的,只是線程里的一個(gè)函數(shù),而不是多線程的。如果直接調(diào)用run(),其實(shí)

就相當(dāng)于是調(diào)用了一個(gè)普通函數(shù)而已,直接待用run()方法必須等待run()方法執(zhí)行完畢才能執(zhí)行下

面的代碼,所以執(zhí)行路徑還是只有一條,根本就沒(méi)有線程的特征,所以在多線程執(zhí)行時(shí)要使用

start。方法而不是run()方法。

20.為什么我們調(diào)用start()方法時(shí)會(huì)執(zhí)行run。方法,為什么我們不能直接調(diào)用

run()方法?

這是另一個(gè)非常經(jīng)典的java多線程面試問(wèn)題,而且在面試中會(huì)經(jīng)常被問(wèn)到。很簡(jiǎn)單,但是很多人都會(huì)

答不上來(lái)!

?new一個(gè)Thread,線程進(jìn)入了新建狀態(tài)。調(diào)用start。方法,會(huì)啟動(dòng)一個(gè)線程并使線程進(jìn)入了就緒

狀態(tài),當(dāng)分配到時(shí)間片后就可以開(kāi)始運(yùn)行了。start。會(huì)執(zhí)行線程的相應(yīng)準(zhǔn)備工作,然后自動(dòng)執(zhí)行

run()方法的內(nèi)容,這是真正的多線程工作。

?而直接執(zhí)行run()方法,會(huì)把run方法當(dāng)成—main線程下的普通方法去執(zhí)行,并不會(huì)在某個(gè)線

程中執(zhí)行它,所以這并不是多線程工作。

總結(jié):調(diào)用start方法方可啟動(dòng)線程并使線程進(jìn)入就緒狀態(tài),而run方法只是thread的一個(gè)普通方法

調(diào)用,還是在主線程里執(zhí)行。

21.什么是Callable和Future?

?Callable接口類(lèi)似于Runnable,從名字就可以看出來(lái)了,但是Runnable不會(huì)返回結(jié)果,并且無(wú)

法拋出返回結(jié)果的異常,而Callable功能更強(qiáng)大一些,被線程執(zhí)行后,可以返回值,這個(gè)返回值

可以被Future拿到,也就是說(shuō),F(xiàn)uture可以拿到異步執(zhí)行任務(wù)的返回值。

?Future接口表示異步任務(wù),是一可能還沒(méi)有完成的異步任務(wù)的結(jié)果.所以說(shuō)Callable用于產(chǎn)生

結(jié)果,F(xiàn)uture用于獲取結(jié)果。

22.什么是FutureTask

?FutureTask表示一個(gè)異步運(yùn)算的任務(wù)。FutureTask里面可以傳入一個(gè)Capable的具體實(shí)現(xiàn)類(lèi),可

以對(duì)這個(gè)異步運(yùn)算的任務(wù)的結(jié)果進(jìn)行等待獲取、判斷是否已經(jīng)完成、取消任務(wù)等操作。只有當(dāng)運(yùn)算

完成的時(shí)候結(jié)果才能取回,如果運(yùn)算尚未完成get方法將會(huì)阻塞。一個(gè)FutureTask對(duì)象可以對(duì)調(diào)

用了Callable和Runnable的對(duì)象進(jìn)行包裝,由于FutureTask也是Runnable接口的實(shí)現(xiàn)類(lèi),所

以FutureTask也可以放入線程池中。

23.線程的狀態(tài)

被其他線程喚醒:

不會(huì)釋放鎖

?新建(new):新創(chuàng)建了一個(gè)線程對(duì)象。

?就緒(可運(yùn)行狀態(tài))(runnable):線程對(duì)象創(chuàng)建后,當(dāng)調(diào)用線程對(duì)象的start。方法,該線程處于就

緒狀態(tài),等待被線程調(diào)度選中,獲取cpu的使用權(quán)。

?運(yùn)行(running):可運(yùn)行狀態(tài)(runnable)的線程獲得了cpu時(shí)間片(timeslice),執(zhí)行程序代碼。

注:就緒狀態(tài)是進(jìn)入到運(yùn)行狀態(tài)的唯一入口,也就是說(shuō),線程要想進(jìn)入運(yùn)行狀態(tài)執(zhí)行,首先必須處

于就緒狀態(tài)中;

?阻塞(block):處于運(yùn)行狀態(tài)中的線程由于某種原因,暫時(shí)放棄對(duì)CPU的使用權(quán),停止執(zhí)行,止匕時(shí)

進(jìn)入阻塞狀態(tài),直到其進(jìn)入到就緒狀態(tài),才有機(jī)會(huì)再次被CPU調(diào)用以進(jìn)入到運(yùn)行狀態(tài)。

。阻塞的情況分三種:

。(一).等待阻塞:運(yùn)行狀態(tài)中的線程執(zhí)行wait。方法,JVM會(huì)把該線程放入等待隊(duì)列(waitting

queue)中,使本線程進(jìn)入到等待阻塞狀態(tài);

。(二).同步阻塞:線程在獲取synchronized同步鎖失敗(因?yàn)殒i被其它線程所占用),,貝!JJVM

會(huì)把該線程放入鎖池(lockpool)*,線程會(huì)進(jìn)入同步阻塞狀態(tài);

。(三).其他阻塞:通過(guò)調(diào)用線程的sleep?;騤oin?;虬l(fā)出了I/O請(qǐng)求時(shí),線程會(huì)進(jìn)入到阻塞狀

態(tài)。當(dāng)sleep。狀態(tài)超時(shí)、join()等待線程終止或者超時(shí)、或者I/O處理完畢時(shí),線程重新轉(zhuǎn)入

就緒狀態(tài)。

?死亡(dead)(結(jié)束):線程run()、main。方法執(zhí)行結(jié)束,或者因異常退出了run()方法,則該線程結(jié)束

生命周期。死亡的線程不可再次復(fù)生。

24.Java中用到的線程調(diào)度算法是什么?

?計(jì)算機(jī)通常只有一個(gè)CPU,在任意時(shí)刻只能執(zhí)行一條機(jī)器指令,每個(gè)線程只有獲得CPU的使用權(quán)

才能執(zhí)行指令。所謂多線程的并發(fā)運(yùn)行,其實(shí)是指從宏觀上看,各個(gè)線程輪流獲得CPU的使用

權(quán),分別執(zhí)行各自的任務(wù)。在運(yùn)行池中,會(huì)有多個(gè)處于就緒狀態(tài)的線程在等待CPU,JAVA虛擬機(jī)

的一項(xiàng)任務(wù)就是負(fù)責(zé)線程的調(diào)度,線程調(diào)度是指按照特定機(jī)制為多個(gè)線程分配CPU的使用權(quán)。

(Java是由JVM中的線程計(jì)數(shù)器來(lái)實(shí)現(xiàn)線程調(diào)度)

?有兩種調(diào)度模型:分時(shí)調(diào)度模型和搶占式調(diào)度模型。

。分時(shí)調(diào)度模型是指讓所有的線程輪流獲得cpu的使用權(quán),并且平均分配每個(gè)線程占用的CPU

的時(shí)間片這個(gè)也比較好理解。

。Java虛擬機(jī)采用搶占式調(diào)度模型,是指優(yōu)先讓可運(yùn)行池中優(yōu)先級(jí)高的線程占用CPU,如果可

運(yùn)行池中的線程優(yōu)先級(jí)相同,那么就隨機(jī)選擇一個(gè)線程,使其占用CPU。處于運(yùn)行狀態(tài)的線

程會(huì)一直運(yùn)行,直至它不得不放棄CPU。

25.線程的調(diào)度策略

線程調(diào)度器選擇優(yōu)先級(jí)最高的線程運(yùn)行,但是,如果發(fā)生以下情況,就會(huì)終止線程的運(yùn)行:

?(1)線程體中調(diào)用了yield方法讓出了對(duì)cpu的占用權(quán)利

?(2)線程體中調(diào)用了sleep方法使線程進(jìn)入睡眠狀態(tài)

?(3)線程由于I0操作受到阻塞

?(4)另夕一個(gè)更高優(yōu)先級(jí)線程出現(xiàn)

?(5)在支持時(shí)間片的系統(tǒng)中,該線程的時(shí)間片用完

26.什么是線程調(diào)度器(ThreadScheduler)和時(shí)間分片(TimeSlicing)?

?線程調(diào)度器是一個(gè)操作系統(tǒng)服務(wù),它負(fù)責(zé)為Runnable狀態(tài)的線程分配CPU時(shí)間。一旦我們創(chuàng)建

一個(gè)線程并啟動(dòng)它,它的執(zhí)行便依賴(lài)于線程調(diào)度器的實(shí)現(xiàn).

?時(shí)間分片是指將可用的CPU時(shí)間分配給可用的Runnable線程的過(guò)程。分配CPU時(shí)間可以基于線

程優(yōu)先級(jí)或者線程等待的時(shí)間。

?線程調(diào)度并不受到Java虛擬機(jī)控制,所以由應(yīng)用程序來(lái)控制它是更好的選擇(也就是說(shuō)不要讓你

的程序依賴(lài)于線程的優(yōu)先級(jí))。

27.請(qǐng)說(shuō)出與線程同步以及線程調(diào)度相關(guān)的方法。

?(Dwait():使一個(gè)線程處于等待(阻塞)狀態(tài),并且釋放所持有的對(duì)象的鎖;

?(2)sleep。:使一個(gè)正在運(yùn)行的線程處于睡眠狀態(tài),是一個(gè)靜態(tài)方法,調(diào)用此方法要處理

InterruptedException異常;

?(3)notify。:?jiǎn)拘岩粋€(gè)處于等待狀態(tài)的線程,當(dāng)然在調(diào)用此方法的時(shí)候,并不能確切的喚醒某一

個(gè)等待狀態(tài)的線程,而是由JVM確定喚醒哪個(gè)線程,而且與優(yōu)先級(jí)無(wú)關(guān);

?(4)notityAIIO:?jiǎn)拘阉刑幱诘却隣顟B(tài)的線程,該方法并不是將對(duì)象的鎖給所有線程,而是讓它

們競(jìng)爭(zhēng),只有獲得鎖的線程才能進(jìn)入就緒狀態(tài);

28.sleep()和wait()有什么區(qū)別?

兩者都可以暫停線程的執(zhí)行

?類(lèi)的不同:sleep。是Thread線程類(lèi)的靜態(tài)方法,wait。是Object類(lèi)的方法。

?是否釋放鎖:sleep。不釋放鎖;wait。釋放鎖。

?用途不同:Wait通常被用于線程間交互/通信,sleep通常被用于暫停執(zhí)行。

?用法不同:wait()方法被調(diào)用后,線程不會(huì)自動(dòng)蘇醒,需要?jiǎng)e的線程調(diào)用同一個(gè)對(duì)象上的notify。

或者notifyAII()方法。sleep。方法執(zhí)行完成后,線程會(huì)自動(dòng)蘇醒?;蛘呖梢允褂脀ait(long

timeout)超時(shí)后線程會(huì)自動(dòng)蘇醒。

29.你是如何調(diào)用wait。方法的?使用if塊還是循環(huán)?為什么?

?處于等待狀態(tài)的線程可能會(huì)收到錯(cuò)誤警報(bào)和偽喚醒,如果不在循環(huán)中檢杳等待條件,程序就會(huì)在沒(méi)

有滿(mǎn)足結(jié)束條件的情況下退出。

?wait。方法應(yīng)該在循環(huán)調(diào)用,因?yàn)楫?dāng)線程獲取到CPU開(kāi)始執(zhí)行的時(shí)候,其他條件可能還沒(méi)有滿(mǎn)

足,所以在處理前,循環(huán)檢測(cè)條件是否滿(mǎn)足會(huì)更好。下面是一段標(biāo)準(zhǔn)的使用wait和notify方法的

代碼:

synchronized(monitor){

//判斷條件謂詞是否得到滿(mǎn)足

while(!locked){

//等待喚醒

monitor;

)

//處理其他的業(yè)務(wù)邏輯

30.為什么線程通信的方法wait(),notify。和notifyAII。被定義在Object類(lèi)里?

?因?yàn)镴ava所有類(lèi)的都繼承了Object,Java想讓任何對(duì)象都可以作為鎖,并且wait(),notify。等方法

用于等待對(duì)象的鎖或者喚醒線程,在Java的線程中并沒(méi)有可供任何對(duì)象使用的鎖,所以任意對(duì)象

調(diào)用方法一定定義在Object類(lèi)中。

?有的人會(huì)說(shuō),既然是線程放棄對(duì)象鎖,那也可以把wait。定義在Thread類(lèi)里面啊,新定義的線程繼

承于Thread類(lèi),也不需要重新定義wait()方法的實(shí)現(xiàn)。然而,這樣做有一個(gè)非常大的問(wèn)題,一個(gè)線

程完全可以持有很多鎖,你一個(gè)線程放棄鎖的時(shí)候,到底要放棄哪個(gè)鎖?當(dāng)然了,這種設(shè)計(jì)并不是

不能實(shí)現(xiàn),只是管理起來(lái)更加復(fù)雜。

31.為什么wait。,notify。和notifyAH。必須在同步方法或者同步塊中被調(diào)用?

?當(dāng)一個(gè)線程需要調(diào)用對(duì)象的wait。方法的時(shí)候,這個(gè)線程必須擁有該對(duì)象的鎖,接著它就會(huì)釋放這

個(gè)對(duì)象鎖并進(jìn)入等待狀態(tài)直到其他線程調(diào)用這個(gè)對(duì)象上的notify。方法。同樣的,當(dāng)一個(gè)線程需要

調(diào)用對(duì)象的notify。方法時(shí),它會(huì)釋放這個(gè)對(duì)象的鎖,以便其他在等待的線程就可以得到這個(gè)對(duì)象

鎖。由于所有的這些方法都需要線程持有對(duì)象的鎖,這樣就只能通過(guò)同步來(lái)實(shí)現(xiàn),所以他們只能在

同步方法或者同步塊中被調(diào)用。

32.Thread類(lèi)中的yield方法有什么作用?

?使當(dāng)前線程從執(zhí)行狀態(tài)(運(yùn)行狀態(tài))變?yōu)榭蓤?zhí)行態(tài)(就緒狀態(tài)).

?當(dāng)前線程到了就緒狀態(tài),那么接下來(lái)哪個(gè)線程會(huì)從就緒狀態(tài)變成執(zhí)行狀態(tài)呢?可能是當(dāng)前線程,也

可能是其他線程,看系統(tǒng)的分配了。

33.為什么Thread類(lèi)的sleep。和yield()方法是靜態(tài)的?

?Thread類(lèi)的sleep。和yield。方法將在當(dāng)前正在執(zhí)行的線程上運(yùn)行。所以在其他處于等待狀態(tài)的線

程上調(diào)用這些方法是沒(méi)有意義的。這就是為什么這些方法是靜態(tài)的。它們可以在當(dāng)前正在執(zhí)行的線

程中工作,并避免程序員錯(cuò)誤的認(rèn)為可以在其他^運(yùn)行線程調(diào)用這些方法。

34.線程的sleep。方法和yield。方法有什么區(qū)別?

?(Dsleep。方法給其他線程運(yùn)行機(jī)會(huì)時(shí)不考慮線程的優(yōu)先級(jí),因此會(huì)給低優(yōu)先級(jí)的線程以運(yùn)行的

機(jī)會(huì);yield。方法只會(huì)給相同優(yōu)先級(jí)或更高優(yōu)先級(jí)的線程以運(yùn)行的機(jī)會(huì);

?(2)線程執(zhí)行sleep。方法后轉(zhuǎn)入阻塞(blocked)狀態(tài),而執(zhí)行yield。方法后轉(zhuǎn)入就緒

(ready)狀態(tài);

?(3)sleep。方法聲明拋出InterruptedException,而yield。方法沒(méi)有聲明任何異常;

?(4)sleep。方法比yield。方法(跟操作系統(tǒng)CPU調(diào)度相關(guān))具有更好的可移植性,通常不建議

使用yield。方法來(lái)控制并發(fā)線程的執(zhí)行。

35.如何停止一個(gè)正在運(yùn)行的線程?

?在java中有以下3種方法可以終止正在運(yùn)行的線程:

。使用退出標(biāo)志,使線程正常退出,也就是當(dāng)run方法完成后線程終止。

。使用Stop方法強(qiáng)行終止,但是不推薦這個(gè)方法,因?yàn)閟top和suspend及resume一樣都是過(guò)期

作廢的方法。

。使用interrupt方法中斷線程。

36.Java中interrupted和islnterrupted方法的區(qū)別?

?interrupt:用于中斷線程。調(diào)用該方法的線程的狀態(tài)為將被置為"中斷"狀態(tài)。

注意:線程中斷僅僅是置線程的中斷狀態(tài)位,不會(huì)停止線程。需要用戶(hù)自己去監(jiān)視線程的狀態(tài)為并做處

理。支持線程中斷的方法他就是線程中斷后會(huì)拋出interruptedException的方法)就是在監(jiān)視線程的

中斷狀態(tài),一旦線程的中斷狀態(tài)被置為"中斷狀態(tài)",就會(huì)拋出中斷異常.

?interrupted:是靜態(tài)方法,查看當(dāng)前中斷信號(hào)是true還是faIse并且清除中斷信號(hào)。如果一個(gè)線程

被中斷了,第一次調(diào)用interrupted則返回true,第二次和后面的就返回false了。

?islnterrupted:是可以返回當(dāng)前中斷信號(hào)是true還是false,與interrupt最大的差別

37.什么是阻塞式方法?

?阻塞式方法是指程序會(huì)一直等待該方法完成期間不做其他事情,Serversocket的accept。方法就是

一直等待客戶(hù)端連接.這里的阻塞是指調(diào)用結(jié)果返回之前,當(dāng)前線程會(huì)被掛起,直到得到結(jié)果之后

才會(huì)返回。此外,還有異步和非阻塞式方法在任務(wù)完成前就返回。

38.Java中你怎樣喚醒一個(gè)阻塞的線程?

?首先,wait。、notify。方法是針對(duì)對(duì)象的,調(diào)用任意對(duì)象的wait。方法都將導(dǎo)致線程阻塞,阻塞

的同時(shí)也將釋放該對(duì)象的鎖,相應(yīng)地,調(diào)用任意對(duì)象的notify。方法則將隨機(jī)解除該對(duì)象阻塞的線

程,但它需要重新獲取該對(duì)象的鎖,直到獲取成功才能往下執(zhí)行;

?其次,wait、notify方法必須在synchronized塊或方法中被調(diào)用,并且要保證同步塊或方法的鎖

對(duì)象與調(diào)用wait、notify方法的對(duì)象是同一個(gè),如此一來(lái)在調(diào)用wait之前當(dāng)前線程就已經(jīng)成功獲

取某對(duì)象的鎖,執(zhí)行wait阻塞后當(dāng)前線程就將之前獲取的對(duì)象鎖釋放。

39.notifyO和notifyAIK)有什么區(qū)別?

?如果線程調(diào)用了對(duì)象的wait()方法,那么線程便會(huì)處于該對(duì)象的等待池中,等待池中的線程不會(huì)去

競(jìng)爭(zhēng)該對(duì)象的鎖。

?notifyAIK)會(huì)喚醒所有的線程,notify。只會(huì)喚醒一個(gè)線程。

?notifyAIK)調(diào)用后,會(huì)將全部線程由等待池移到鎖池,然后參與鎖的競(jìng)爭(zhēng),競(jìng)爭(zhēng)成功則繼續(xù)執(zhí)行,

如果不成功則留在鎖池等待鎖被釋放后再次參與競(jìng)爭(zhēng)。而notify。只會(huì)喚醒一個(gè)線程,具體喚醒哪

一個(gè)線程由虛擬機(jī)控制。

40.如何在兩個(gè)線程間共享數(shù)據(jù)?

?在兩個(gè)線程間共享變量即可實(shí)現(xiàn)共享。

一般來(lái)說(shuō),共享變量要求變量本身是線程安全的,然后在線程內(nèi)使用的時(shí)候,如果有對(duì)共享變量的復(fù)合操作,那么

也得保證復(fù)合操作的線程安全性。

41.Java如何實(shí)現(xiàn)多線程之間的通訊和協(xié)作?

?可以通過(guò)中斷和共享變量的方式實(shí)現(xiàn)線程間的通訊和I協(xié)作

?比如說(shuō)最經(jīng)典的生產(chǎn)者-消費(fèi)者模型:當(dāng)隊(duì)列滿(mǎn)時(shí),生產(chǎn)者需要等待隊(duì)列有空間才能繼續(xù)往里面放

入商品,而在等待的期間內(nèi),生產(chǎn)者必須釋放對(duì)臨界資源(即隊(duì)列)的占用權(quán).因?yàn)樯a(chǎn)者如果不

釋放對(duì)臨界資源的占用權(quán),那么消費(fèi)者就無(wú)法消費(fèi)隊(duì)列中的商品,就不會(huì)讓隊(duì)列有空間,那么生產(chǎn)

者就會(huì)一直無(wú)限等待下去。因此,一般情況下,當(dāng)隊(duì)列滿(mǎn)時(shí),會(huì)讓生產(chǎn)者交出對(duì)臨界資源的占用

權(quán),并進(jìn)入掛起狀態(tài).然后等待消費(fèi)者消費(fèi)了商品,然后消費(fèi)者通知生產(chǎn)者隊(duì)列有空間了。同樣

地,當(dāng)隊(duì)列空時(shí),消費(fèi)者也必須等待,等待生產(chǎn)者通知它隊(duì)列中有商品了。這種互相通信的過(guò)程就

是線程間的協(xié)作。

?Java中線程通信協(xié)作的最常見(jiàn)方式:

o—.syncrhoized加鎖的線程的Object類(lèi)的wait()/notify()/notifyAII()

■二.ReentrantLock類(lèi)力口鎖的線程的Condition類(lèi)的await()/signal()/signalAII()

?線程間直接的數(shù)據(jù)交換:

。三.通過(guò)管道進(jìn)行線程間通信:字節(jié)流、字符流

42.同步方法和同步塊,哪個(gè)是更好的選擇?

?同步塊是更好的選擇,因?yàn)樗粫?huì)鎖住整個(gè)對(duì)象(當(dāng)然你也可以讓它鎖住整個(gè)對(duì)象).同步方法會(huì)

鎖住整個(gè)對(duì)象,哪怕這個(gè)類(lèi)中有多個(gè)不相關(guān)聯(lián)的同步塊,這通常會(huì)導(dǎo)致他們停止執(zhí)行并需要等待獲

得這個(gè)對(duì)象上的鎖。

?同步塊更要符合開(kāi)放調(diào)用的原則,只在需要鎖住的代碼塊鎖住相應(yīng)的對(duì)象,這樣從側(cè)面來(lái)說(shuō)也可以

避免死鎖。

請(qǐng)知道一條原則:同步的范圍越小越好。

43.什么是線程同步和線程互斥,有哪幾種實(shí)現(xiàn)方式?

?當(dāng)一個(gè)線程對(duì)共享的數(shù)據(jù)進(jìn)行操作時(shí),應(yīng)使之成為一個(gè)"原子操作",即在沒(méi)有完成相關(guān)操作之前,

不允許其他線程打斷它,否則,就會(huì)破壞數(shù)據(jù)的完整性,必然會(huì)得到錯(cuò)誤的處理結(jié)果,這就是線程

的同步。

?在多線程應(yīng)用中,考慮不同線程之間的數(shù)據(jù)同步和防止死鎖。當(dāng)兩個(gè)或多個(gè)線程之間同時(shí)等待對(duì)方

釋放資源的時(shí)候就會(huì)形成線程之間的死鎖。為了防止死鎖的發(fā)生,需要通過(guò)同步來(lái)實(shí)現(xiàn)線程安全。

?線程互斥是指對(duì)于共享的進(jìn)程系統(tǒng)資源,在各單個(gè)線程訪問(wèn)時(shí)的排它性。當(dāng)有若干個(gè)線程都要使用

某一共享資源時(shí),任何時(shí)刻最多只允許一個(gè)線程去使用,其它要使用該資源的線程必須等待,直到

占用資源者釋放該資源。線程互斥可以看成是一種特殊的線程同步。

?線程間的同步方法大體可分為兩類(lèi):用戶(hù)模式和內(nèi)核模式。顧名思義,內(nèi)核模式就是指利用系統(tǒng)內(nèi)

核對(duì)象的單一性來(lái)進(jìn)行同步,使用時(shí)需要切換內(nèi)核態(tài)與用戶(hù)態(tài),而用戶(hù)模式就是不需要切換到內(nèi)核

態(tài),只在用戶(hù)態(tài)完成操作。

?用戶(hù)模式下的方法有:原子操作(例如一個(gè)單一的全局變量),臨界區(qū)。內(nèi)核模式下的方法有:事

件,信號(hào)量,互斥量。

?實(shí)現(xiàn)線程同步的方法

。同步代碼方法:sychronized關(guān)鍵字修飾的方法

。同步代碼塊:sychronized關(guān)鍵字修飾的代碼塊

。使用特殊變量域volatile實(shí)現(xiàn)線程同步:volatile關(guān)犍字為域變量的訪問(wèn)提供了一種免鎖機(jī)制

。使用重入鎖實(shí)現(xiàn)線程同步:reentrantlock類(lèi)是可沖入、互斥、實(shí)現(xiàn)了lock接口的鎖他與

sychronized方法具有相同的基本行為和語(yǔ)義

44.在監(jiān)視器(Monitor)內(nèi)部,是如何做線程同步的?程序應(yīng)該做哪種級(jí)別的同

步?

?在java虛擬機(jī)中,監(jiān)視器和鎖在Java虛擬機(jī)中是一塊使用的。監(jiān)視器監(jiān)視一塊同步代碼塊,確保

一次只有一個(gè)線程執(zhí)行同步代碼塊。每一個(gè)監(jiān)視器都和一個(gè)對(duì)象引用相關(guān)聯(lián)。線程在獲取鎖之前不

允許執(zhí)行同步代碼。

?一旦方法或者代碼塊被synchronized修飾,那么這個(gè)部分就放入了監(jiān)視器的監(jiān)視區(qū)域,確保一次

只能有一個(gè)線程執(zhí)行該部分的代碼,線程在獲取鎖之前不允許執(zhí)行該部分的代碼

?另外java還提供了顯式監(jiān)視器(Lock)和隱式監(jiān)視器(synchronized)兩種鎖方案

45.如果你提交任務(wù)時(shí),線程池隊(duì)列已滿(mǎn),這時(shí)會(huì)發(fā)生什么

?有倆種可能:

(1)如果使用的是無(wú)界隊(duì)歹ULinkedBlockingQueue,也就是無(wú)界隊(duì)列的話,沒(méi)關(guān)系,繼續(xù)添加任務(wù)到

阻塞隊(duì)列中等待執(zhí)行,因?yàn)長(zhǎng)inkedBlockingQueue可以近乎認(rèn)為是一個(gè)無(wú)窮大的隊(duì)列,可以無(wú)限存放

彳鎊

(2)如果使用的是有界隊(duì)列比如ArrayBlockingQueue,任務(wù)首先會(huì)被添加到ArrayBlockingQueue

中,ArrayBlockingQueue滿(mǎn)了,會(huì)根據(jù)maximumPoolSize的值增加線程數(shù)量,如果增加了線程數(shù)量

還是處理不過(guò)來(lái),ArrayBlockingQueue繼續(xù)滿(mǎn),那么則會(huì)使用拒絕策略RejectedExecutionHandler

處理滿(mǎn)了的任務(wù),默認(rèn)是AbortPolicy

46.什么叫線程安全?servlet是線程安全嗎?

?線程安全是編程中的術(shù)語(yǔ),指某個(gè)方法在多線程環(huán)境中被調(diào)用時(shí),能夠正確地處理多個(gè)線程之間的

共享變量,使程序功能正確完成。

?Servlet不是線程安全的,servlet是單實(shí)例多線程的,當(dāng)多個(gè)線程同時(shí)訪問(wèn)同f方法,是不能保

證共享變量的線程安全性的。

?Struts2的action是多實(shí)例多線程的,是線程安全的,每個(gè)請(qǐng)求過(guò)來(lái)都會(huì)new一個(gè)新的action分

配給這個(gè)請(qǐng)求,請(qǐng)求完成后銷(xiāo)毀。

?SpringMVC的Controller是線程安全的嗎?不是的,和Servlet類(lèi)似的處理流程。

?Struts2好處是不用考慮線程安全問(wèn)題;Servlet和SpringMVC需要考慮線程安全問(wèn)題,但是性能

可以提升不用處理太多的gc,可以使用ThreadLocal來(lái)處理多線程的問(wèn)題。

47.在Java程序中怎么保證多線程的運(yùn)行安全?

?方法一:使用安全類(lèi),比如java.util.concurrent下的類(lèi),使用原子類(lèi)Atomiclnteger

?方法二:使用自動(dòng)鎖synchronized。

?方法三:使用手動(dòng)鎖Lock。

?手動(dòng)鎖Java示例代碼如下:

Locklock=newReentrantLock();

lock.lock();

try{

System,out.printing獲得鎖”);

}catch(Exceptione){

//TODO:handleexception

}finally{

System.out.printin(“釋放鎖”);

lock.unlock();

)

48.你對(duì)線程優(yōu)先級(jí)的理解是什么?

?每一個(gè)線程都是有優(yōu)先級(jí)的,一般來(lái)說(shuō),高優(yōu)先級(jí)的線程在運(yùn)行時(shí)會(huì)具有優(yōu)先權(quán),但這依賴(lài)于線程

調(diào)度的實(shí)現(xiàn),這個(gè)實(shí)現(xiàn)是和操作系統(tǒng)相關(guān)的(OSdependent)。我們可以定義線程的優(yōu)先級(jí),但是

這并不能保證高優(yōu)先級(jí)的線程會(huì)在低優(yōu)先級(jí)的線程前執(zhí)行。線程優(yōu)先級(jí)是一個(gè)int變量(從1-10),

1代表最低優(yōu)先級(jí),10代表最高優(yōu)先級(jí)。

?Java的線程優(yōu)先級(jí)調(diào)度會(huì)委托給操作系統(tǒng)去處理,所以與具體的操作系統(tǒng)優(yōu)先級(jí)有關(guān),如非特別

需要,一般無(wú)需設(shè)置線程優(yōu)先級(jí)。

?當(dāng)然,如果你真的想設(shè)置優(yōu)先級(jí)可以通過(guò)setPriority()方法設(shè)置,但是設(shè)置了不一定會(huì)該變,這個(gè)

是不準(zhǔn)確的

49.線程類(lèi)的構(gòu)造方法、靜態(tài)塊是被哪個(gè)線程調(diào)用的

?這是一個(gè)非常刁鉆和狡猾的問(wèn)題。請(qǐng)記?。壕€程類(lèi)的構(gòu)造方法、靜態(tài)塊是被new這個(gè)線程類(lèi)所在

的線程所調(diào)用的,而run方法里面的代碼才是被線程自身所調(diào)用的。

?如果說(shuō)上面的說(shuō)法讓你感到困惑,那么我舉個(gè)例子,假設(shè)Thread2中new了Threadl,main函

數(shù)中new了Thread2,那么:

(1)Thread2的構(gòu)造方法、靜態(tài)塊是main線程調(diào)用的,Thread2的run。方法是Thread2自己調(diào)用的

(2)Thread1的構(gòu)造方法、靜態(tài)塊是Thread2調(diào)用的,Thread1的run。方法是ThreacH自己調(diào)用的

50.Java中怎么獲取一份線程dump文件?你如何在Java中獲取線程堆棧?

?Dump文件是進(jìn)程的內(nèi)存鏡像??梢园殉绦虻膱?zhí)行狀態(tài)通過(guò)調(diào)試器保存到dump文件中。

?在Linux下,你可以通過(guò)命令kill-3PID(Java進(jìn)程的進(jìn)程ID)來(lái)獲取Java應(yīng)用的dump文件。

?在Windows下,你可以按下Ctrl+Break來(lái)獲取。這樣JVM就會(huì)將線程的dump文件打E倒標(biāo)

準(zhǔn)輸出或錯(cuò)誤文件中,它可能打印在控制臺(tái)或者日志文件中,具體位置依賴(lài)應(yīng)用的配置。

51.一個(gè)線程運(yùn)行時(shí)發(fā)生異常會(huì)怎樣?

?如果異常沒(méi)有被捕獲該線程將會(huì)停止執(zhí)行。Thread.UncaughtExceptionHandler是用于處理未捕

獲異常造成線程突然中斷情況的一個(gè)內(nèi)嵌接口。當(dāng)一個(gè)未捕獲異常將造成線程中斷的時(shí)候,JVM

會(huì)使用Thread.getUncaughtExceptionHandler。來(lái)查詢(xún)線程的UncaughtExceptionHandler并將

線程和異常作為參數(shù)傳遞給handler的uncaughtException()方法進(jìn)行處理。

52.Java線程數(shù)過(guò)多會(huì)造成什么異常?

?線程的生命周期開(kāi)銷(xiāo)非常高

?消耗過(guò)多的CPU

資源如果可運(yùn)行的線程數(shù)量多于可用處理器的數(shù)量,那么有線程將會(huì)被閑置。大量空閑的線程會(huì)占

用許多內(nèi)存,給垃圾回收器帶來(lái)壓力,而且大量的線程在競(jìng)爭(zhēng)CPU資源時(shí)還將產(chǎn)生其他性能的開(kāi)

銷(xiāo)。

?降低穩(wěn)定'由VM

在可創(chuàng)建線程的數(shù)量上存在一個(gè)限制,這個(gè)限制值將隨著平臺(tái)的不同而不同,并且承受著多個(gè)因素

制約,包括JVM的啟動(dòng)參數(shù)、Thread構(gòu)造函數(shù)中請(qǐng)求棧的大小,以及底層操作系統(tǒng)對(duì)線程的限制

等。如果破壞了這些限制,那么可能拋出。戊0£1\/16巾?!贰旰埂!府惓?。

53.多線程的常用方法

方法名描述

sleepO強(qiáng)迫一個(gè)線程睡眠N毫秒

isAlive()判哈個(gè)編魄否存活。

join()等待線程終止。

activeCount()程序中活躍的線程數(shù)。

enumerateO枚舉程序中的線程.

currentThreadO得到當(dāng)前線程。

isDaemonO一個(gè)線程是否為守護(hù)線程.

setDaemon()設(shè)置一個(gè)線程為守護(hù)線程。

setName()為線程設(shè)置一個(gè)名稱(chēng)。

wait()強(qiáng)迫一個(gè)線程等待。

notifyO通知一個(gè)線程繼續(xù)運(yùn)行。

setPriorityO設(shè)置一個(gè)線程的優(yōu)先級(jí)。

并發(fā)理論

1.Java中垃圾回收有什么目的?什么時(shí)候進(jìn)行垃圾回收?

?垃圾回收是在內(nèi)存中存在沒(méi)有引用的對(duì)象或超過(guò)作用域的對(duì)象時(shí)進(jìn)行的.

?垃圾回收的目的是識(shí)別并且丟棄應(yīng)用不再使用的對(duì)象來(lái)釋放和重用資源。

2.線程之間如何通信及線程之間如何同步

?在并發(fā)編程中,我們需要處理兩個(gè)關(guān)鍵問(wèn)題:線程之間如何通信及線程之間如何同步。通信是指線

程之間以如何來(lái)交換信息。一般線程之間的通信機(jī)制有兩種:共享內(nèi)存和消息傳遞。

?Java的并發(fā)采用的是共享內(nèi)存模型,Java線程之間的通信總是隱式進(jìn)行,整個(gè)通信過(guò)程對(duì)程序員完

全透明。如果編寫(xiě)多線程程序的Java程序員不理解隱式進(jìn)行的線程之間通信的工作機(jī)制,很可能會(huì)

遇到各種奇怪的內(nèi)存可見(jiàn)性問(wèn)題。

3.Java內(nèi)存模型

?共享內(nèi)存模型指的就是Java內(nèi)存模型(簡(jiǎn)稱(chēng)JMM),JMM決定一個(gè)線程對(duì)共享變量的寫(xiě)入時(shí),能對(duì)另一

個(gè)線程可見(jiàn)。從抽象的角度來(lái)看,JMM定義了線程和主內(nèi)存之間的抽象關(guān)系:線程之間的共享變

量存儲(chǔ)在主內(nèi)存(mainmemory)中,每個(gè)線程都有一個(gè)私有的本地內(nèi)存(localmemory),本

地內(nèi)存中存儲(chǔ)了該線程以讀/寫(xiě)共享變量的副本。本地內(nèi)存是JMM的一個(gè)抽象概念,并不真實(shí)存

在。它涵蓋了緩存,寫(xiě)緩沖區(qū),寄存器以及其他的硬件和編譯器優(yōu)化。

?從上圖來(lái)看,線程A與線程B之間如要通信的話,必須要經(jīng)歷下面2個(gè)步驟:

1.首先,線程A把本地內(nèi)存A中更新過(guò)的共享變量刷新到主內(nèi)存中去。

2.然后,線程B到主內(nèi)存中去讀取線程A之前已更新過(guò)的共享變量。

下面通過(guò)示意圖來(lái)說(shuō)明線程之間的通信

主內(nèi)存

:c-l

?總結(jié):什么是Java內(nèi)存模型:java內(nèi)存模型簡(jiǎn)稱(chēng)jmm,定義了一個(gè)線程對(duì)另一個(gè)線程可見(jiàn)。共享變

量存放在主內(nèi)存中,每個(gè)線程都有自己的本地內(nèi)存,當(dāng)多個(gè)線程同時(shí)訪問(wèn)一個(gè)數(shù)據(jù)的時(shí)候,可能本

地內(nèi)存沒(méi)有及時(shí)刷新到主內(nèi)存,所以就會(huì)發(fā)生線程安全問(wèn)題。

4.如果對(duì)象的引用被置為null,垃圾收集器是否會(huì)立即釋放對(duì)象占用的內(nèi)存?

?不會(huì),在下一個(gè)垃圾回調(diào)周期中,這個(gè)對(duì)象將是被可回收的。

?也就是說(shuō)并不會(huì)立即被垃圾收集器立刻回收,而是在下一次垃圾回收時(shí)才會(huì)釋放其占用的內(nèi)存。

5.finalize。方法什么時(shí)候被調(diào)用?析構(gòu)函數(shù)(finalization)的目的是什么?

?1.垃圾回收器(garbagecolector)決定回收某對(duì)象時(shí),就會(huì)運(yùn)行該對(duì)象的finalize。方法;

finalize是Object類(lèi)的一?"方法,該方法在Object類(lèi)中的聲明protectedvoidfinalize()throws

Throwable{)在垃圾回收器執(zhí)行時(shí)會(huì)調(diào)用被回收對(duì)象的finalize。方法,可以覆蓋此方法來(lái)實(shí)現(xiàn)對(duì)

其資源的回收。注意:一旦垃圾回收器準(zhǔn)備釋放對(duì)象占用的內(nèi)存,將首先調(diào)用該對(duì)象的finalize。方

法,并且下一次垃圾回收動(dòng)作發(fā)生時(shí),才真正回收對(duì)象占用的內(nèi)存空間

?1.GC本來(lái)就是內(nèi)存回收了,應(yīng)用還需要在finalization做什么呢?答案是大部分時(shí)候,什么都

不用做(也就是不需要重載)。只有在某些很特殊的情況下,比如你調(diào)用了一些native的方法

(一般是C寫(xiě)的),可以要在finaliztion里去調(diào)用C的釋放函數(shù)。

。Finalizetion主要用來(lái)釋放被對(duì)象占用的資源(不是指內(nèi)存,而是指其他資源,比如文件(File

Handle),端口(ports)、數(shù)據(jù)庫(kù)連接(DBConnection)等)。然而,它不能真正有效地工作。

6.什么是重排序

?程序執(zhí)行的順序按照代碼的先后I放序執(zhí)行。

?一般來(lái)說(shuō)處理器為了提高程序運(yùn)行效率,可能會(huì)對(duì)輸入代碼進(jìn)行優(yōu)化,進(jìn)行重新排序(重排序),

它不保證程序中各個(gè)語(yǔ)句的執(zhí)行先后順序同代碼中的W頁(yè)序一致,但是它會(huì)保證程序最終執(zhí)行結(jié)果和

代碼順序執(zhí)行的結(jié)果是一致的。

inta=5//語(yǔ)句1

intr=3//語(yǔ)句2

a=a+2//語(yǔ)句3

r=a*a;//語(yǔ)句4

?則因?yàn)橹嘏判?,他還可能執(zhí)行順序?yàn)椋ㄟ@里標(biāo)注的是語(yǔ)句的執(zhí)行順序)2-1-3-4,1-3-2-4但絕不

可能2-1-4-3,因?yàn)檫@打破了依賴(lài)關(guān)系。

?顯然重排序?qū)尉€程運(yùn)行是不會(huì)有任何問(wèn)題,但是多線程就不一定了,所以我們?cè)诙嗑€程編程時(shí)就

得考慮這個(gè)問(wèn)題了。

7.重排序?qū)嶋H執(zhí)行的指令步驟

1.編譯器優(yōu)化的重排序。編譯器在不改變單線程程序語(yǔ)義的前提下,可以重新安排語(yǔ)句的執(zhí)行順序。

2.指令級(jí)并行的重排序。現(xiàn)代處理器采用了指令級(jí)并行技術(shù)(ILP)來(lái)將多條指令重疊執(zhí)行。如果不

存在數(shù)據(jù)依賴(lài)性,處理器可以改變語(yǔ)句對(duì)應(yīng)機(jī)器指令的執(zhí)行質(zhì)序.

3.內(nèi)存系統(tǒng)的重排序。由于處理器使用緩存和讀/寫(xiě)緩沖區(qū),這使得加載和存儲(chǔ)操作看上去可能是在

亂序執(zhí)行.

?這些重排序?qū)τ趩尉€程沒(méi)問(wèn)題,但是多線程都可能會(huì)導(dǎo)致多線程程序出現(xiàn)內(nèi)存可見(jiàn)性問(wèn)題。

8.重排序遵守的規(guī)則

?as-if-serial:

1.不管怎么排序,結(jié)果不能改變

2.不存在數(shù)據(jù)依賴(lài)的可以被編譯器和處理器重排序

3.一個(gè)操作依賴(lài)兩個(gè)操作,這兩個(gè)操作如果不存在依賴(lài)可以重排序

4.單線程根據(jù)此規(guī)則不會(huì)有問(wèn)題,但是重排序后多線程會(huì)有問(wèn)題

9.as-if-serial規(guī)則和happens-before規(guī)則的區(qū)別

?as-if-serial語(yǔ)義保證單線程內(nèi)程序的執(zhí)行結(jié)果不被改變,h叩pens-before關(guān)系保證正確同步的多

線程程序的執(zhí)行結(jié)果不被改變。

?as-if-serial語(yǔ)義給編寫(xiě)單線程程序的程序員創(chuàng)造了一個(gè)幻境:?jiǎn)尉€程程序是按程序的J1I頁(yè)序來(lái)執(zhí)行

的。happens-before關(guān)系給編寫(xiě)正確同步的多線程程序的程序員創(chuàng)造了一個(gè)幻境:正確同步的多

線程程序是按happens-before指定的順序來(lái)執(zhí)行的.

?as-if-serial語(yǔ)義和happens-before這么做的目的,都是為了在不改變程序執(zhí)行結(jié)果的前提下,盡

可能地提高程序執(zhí)行的并行度。

10.并發(fā)關(guān)鍵字synchronized?

?在Java中,synchronized關(guān)鍵字是用來(lái)控制線程同步的,就是在多線程的環(huán)境下,控制

synchronized代碼段不被多個(gè)線程同時(shí)執(zhí)行。synchronized可以修飾類(lèi)、方法、變量。

?另外,在Java早期版本中,synchronized屬于重量級(jí)鎖,效率低下,因?yàn)楸O(jiān)視器鎖(monitor)

是依賴(lài)于底層的操作系統(tǒng)的MutexLock來(lái)實(shí)現(xiàn)的,Java的線程是映射到操作系統(tǒng)的原生線程之上

的。如果要掛起或者喚醒一個(gè)線程,都需要操作系統(tǒng)幫忙完成,而操作系統(tǒng)實(shí)現(xiàn)線程之間的切換時(shí)

需要從用戶(hù)態(tài)轉(zhuǎn)換到內(nèi)核態(tài),這個(gè)狀態(tài)之間的轉(zhuǎn)換需要相對(duì)比較長(zhǎng)的時(shí)間,時(shí)間成本相對(duì)較高,這

也是為什么早期的synchronized效率低的原因。慶幸的是在Java6之后Java官方對(duì)從JVM層面

對(duì)synchronized較大優(yōu)化,所以現(xiàn)在的synchronized鎖效率也優(yōu)化得很不錯(cuò)了。JDK1.6對(duì)鎖的

實(shí)現(xiàn)引入了大量的優(yōu)化,如自旋鎖、適應(yīng)性自旋鎖、鎖消除、鎖粗化、偏向鎖、輕量級(jí)鎖等技術(shù)來(lái)

減少鎖操作的開(kāi)銷(xiāo)。

11.說(shuō)說(shuō)自己是怎么使用synchronized關(guān)鍵字,在項(xiàng)目中用到了嗎

synchronized關(guān)鍵字最主要的三種使用方式:

?修飾實(shí)例方法:作用于當(dāng)前對(duì)象實(shí)例加鎖,進(jìn)入同步代碼前要獲得當(dāng)前對(duì)象實(shí)例的鎖

?修飾靜態(tài)方法:也就是給當(dāng)前類(lèi)加鎖,會(huì)作用于類(lèi)的所有對(duì)象實(shí)例,因?yàn)殪o態(tài)成員不屬于任何一個(gè)

實(shí)例對(duì)象,是類(lèi)成員(static表明這是該類(lèi)的一個(gè)靜態(tài)資源,不管new了多少個(gè)對(duì)象,只有一

份)。所以如果f線程A調(diào)用一個(gè)實(shí)例對(duì)象的非靜態(tài)synchronized方法,而線程B需要調(diào)用這個(gè)

實(shí)例對(duì)象所屬類(lèi)的靜態(tài)synchronized方法,是允許的,不會(huì)發(fā)生互斥現(xiàn)象,因?yàn)樵L問(wèn)靜態(tài)

synchronized方法占用的鎖是當(dāng)前類(lèi)的鎖,而訪問(wèn)非靜態(tài)synchronized方法占用的鎖是當(dāng)前實(shí)

例對(duì)象鎖。

?修飾代碼塊:指定加鎖對(duì)象,對(duì)給定對(duì)象加鎖,進(jìn)入同步代碼庫(kù)前要獲得給定對(duì)象的鎖。

總結(jié):synchronized關(guān)鍵字加到static靜態(tài)方法和synchronized(class)代碼塊上都是是給Class

類(lèi)上鎖。synchronized關(guān)鍵字加到實(shí)例方法上是給對(duì)象實(shí)例上鎖。盡量不要使用synchronized(String

a)因?yàn)镴VM中,字符串常量池具有緩存功能!

12.單例模式了解嗎?給我解釋一下雙重檢驗(yàn)鎖方式實(shí)現(xiàn)單例模式!

雙重校驗(yàn)鎖實(shí)現(xiàn)對(duì)象單例(線程安全)

說(shuō)明:

?雙鎖機(jī)制的出現(xiàn)是為了解決前面同步問(wèn)題和性能問(wèn)題,看下面的代碼,簡(jiǎn)單分析下確實(shí)是解決了多

線程并行進(jìn)來(lái)不會(huì)出現(xiàn)重復(fù)new對(duì)象,而且也實(shí)現(xiàn)了懶加載

publicclassSingleton{

privatevolatilestaticSingletonuniqueinstance;

privateSingleton(){}

publicstaticSingletongetuniquelnstance(){

//先判斷對(duì)象是否已經(jīng)實(shí)例過(guò),沒(méi)有實(shí)例化過(guò)才進(jìn)入加鎖代碼

if(uniqueinstance==null){

//類(lèi)對(duì)象加鎖

synchronized(Singleton.class){

if(uniqueinstance==null){

uniqueinstance=newSingleton。;

)

)

)

returnuniqueinstance;

)

另外,需要注意uniqueinstance采用volatile關(guān)鍵字修飾也是很有必要。

?uniqueinstance采用volatile關(guān)鍵字修飾也是很有必要的,uniqueinstance=newSingleton();

這段代碼其實(shí)是分為三步執(zhí)行:

1.為uniquelnstance分配內(nèi)存空間

2.初始化uniqueinstance

3.將uniqueinstance指向分配的內(nèi)存地址

但是由于具有指令重排的特性,執(zhí)行順序有可能變成指令重排在單線程環(huán)境下不會(huì)出現(xiàn)問(wèn)題,

JVM1->3->20

但是在多線程環(huán)境下會(huì)導(dǎo)致一個(gè)線程獲得還沒(méi)有初始化的實(shí)例。例如,線程T1執(zhí)行J'1和3,此時(shí)丁2調(diào)用

getuniquelnstancef)后發(fā)現(xiàn)uniqueinstance不為空,因此返回uniqueinstance,但此時(shí)

uniqueinstance還未被初始化。

使用volatile可以禁止JVM的指令重排,保證在多線程環(huán)境下也能正常運(yùn)行。

13.說(shuō)一下synchronized底層實(shí)現(xiàn)原理?

?Synchronized的語(yǔ)義底層是通過(guò)一個(gè)monitor(監(jiān)視器鎖)的對(duì)象來(lái)完成,

?每個(gè)對(duì)象有一個(gè)監(jiān)視器鎖(monitor)。每個(gè)Synchronized修飾過(guò)的代碼當(dāng)它的monitor被占用時(shí)就

會(huì)處于鎖定狀態(tài)并且嘗試獲取monitor的所有權(quán),過(guò)程:

1、如果monitor的進(jìn)入數(shù)為0,貝舷線程進(jìn)入monitor,然后將進(jìn)入數(shù)設(shè)置為1,該線程即為

monitor的所有者。

2、如果線程已經(jīng)占有該monitor,只是重新進(jìn)入,則進(jìn)入monitor的進(jìn)入數(shù)加1.

3、如果其他線程已經(jīng)占用了monitor,則該線程進(jìn)入阻塞狀態(tài),直到monitor的進(jìn)入數(shù)為0,再

重新嘗試獲取monitor的所有權(quán)。

synchronized是可以通過(guò)反匯編指令javap命令,杏看相應(yīng)的字節(jié)碼文件。

14.synchronized可重入的原理

?重入鎖是指一個(gè)線程獲取到該鎖之后,該線程可以繼續(xù)獲得該鎖。底層原理維護(hù)一個(gè)計(jì)數(shù)器,當(dāng)線

程獲取該鎖時(shí),計(jì)數(shù)器加一,再次獲得該鎖時(shí)繼續(xù)加一,釋放鎖時(shí),計(jì)數(shù)器減一,當(dāng)計(jì)數(shù)器值為o

時(shí),表明該鎖未被任何線程所持有,其它線程可以競(jìng)爭(zhēng)獲取鎖。

15.什么是

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶(hù)所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶(hù)上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶(hù)上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶(hù)因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

最新文檔

評(píng)論

0/150

提交評(píng)論