電子書下載地址: http://wiki.jikexueyuan.com/project/java-interview-bible/
1. 下面哪些是Thread類的方法?
A start() B run() C exit() D getPriority()
答案:ABD
解析:看 Java API docs吧:http://docs.oracle.com/javase/7/docs/api/炫七, exit()
是 System 類的方法,如System.exit(0)筹吐。
2. 下面程序的運行結(jié)果?
public static void main(String args[]) {
Thread t = new Thread() {
public void run() {
pong();
}
};
t.run();
System.out.print("ping");
}
static void pong() {
System.out.print("pong");
}
A. pingpong
B. pongping
C. pingpong和pongping都有可能
D. 都不輸出
答案:B
解析:這里考的是 Thread 類中 start() 和 run() 方法的區(qū)別了盾碗。start() 用來啟動一個線程鸳粉,當調(diào)用 start 方法后富纸,系統(tǒng)才會開啟一個新的線程审姓,進而調(diào)用 run() 方法來執(zhí)行任務珍特,而單獨的調(diào)用run() 就跟調(diào)用普通方法是一樣的,已經(jīng)失去線程的特性了魔吐。因此在啟動一個線程的時候一定要使用 start() 而不是 run()扎筒。
3. 進程和線程的區(qū)別是什么?
進程是執(zhí)行著的應用程序酬姆,而線程是進程內(nèi)部的一個執(zhí)行序列嗜桌。一個進程可以有多個線程。線程又叫做輕量級進程辞色。
4. 創(chuàng)建線程有幾種不同的方式骨宠?你喜歡哪一種?為什么?
有三種方式可以用來創(chuàng)建線程:
- 繼承 Thread 類
- 實現(xiàn) Runnable 接口
- 應用程序可以使用 Executor 框架來創(chuàng)建線程池
實現(xiàn) Runnable 接口這種方式更受歡迎层亿,因為這不需要繼承 Thread 類桦卒。在應用設計中已經(jīng)繼承了別的對象的情況下,這需要多繼承(而 Java 不支持多繼承)匿又,只能實現(xiàn)接口方灾。同時,線程池也是非常高效的碌更,很容易實現(xiàn)和使用迎吵。
5. 概括的解釋下線程的幾種可用狀態(tài)。
線程在執(zhí)行過程中针贬,可以處于下面幾種狀態(tài):
- 就緒(Runnable):線程準備運行击费,不一定立馬就能開始執(zhí)行。
- 運行中(Running):進程正在執(zhí)行線程的代碼桦他。
- 等待中(Waiting):線程處于阻塞的狀態(tài)蔫巩,等待外部的處理結(jié)束。
- 睡眠中(Sleeping):線程被強制睡眠快压。
- I/O阻塞(Blocked on I/O):等待I/O操作完成圆仔。
同步阻塞(Blocked on Synchronization):等待獲取鎖。 - 死亡(Dead):線程完成了執(zhí)行蔫劣。
6. 同步方法和同步代碼塊的區(qū)別是什么坪郭?
在 Java 語言中,每一個對象有一把鎖脉幢。線程可以使用 synchronized 關(guān)鍵字來獲取對象上的鎖歪沃。 synchronized 關(guān)鍵字可應用在方法級別(粗粒度鎖)或者是代碼塊級別(細粒度鎖)。
7. 在監(jiān)視器(Monitor)內(nèi)部嫌松,是如何做線程同步的沪曙?程序應該做哪種級別的同步?
監(jiān)視器和鎖在 Java 虛擬機中是一塊使用的萎羔。監(jiān)視器監(jiān)視一塊同步代碼塊液走,確保一次只有一個線程執(zhí)行同步代碼塊。每一個監(jiān)視器都和一個對象引用相關(guān)聯(lián)贾陷。線程在獲取鎖之前不允許執(zhí)行同步代碼缘眶。
8. 什么是死鎖(deadlock)?
兩個進程都在等待對方執(zhí)行完畢才能繼續(xù)往下執(zhí)行的時候就發(fā)生了死鎖髓废。結(jié)果就是兩個進程都陷入了無限的等待中巷懈。
9. 如何確保 N 個線程可以訪問 N 個資源同時又不導致死鎖?
使用多線程的時候瓦哎,一種非常簡單的避免死鎖的方式就是:指定獲取鎖的順序砸喻,并強制線程按照指定的順序獲取鎖柔逼。因此,如果所有的線程都是以同樣的順序加鎖和釋放鎖割岛,就不會出現(xiàn)死鎖了
10. sleep() 和 wait() 有什么區(qū)別?
答:sleep()方法是線程類(Thread)的靜態(tài)方法愉适,導致此線程暫停執(zhí)行指定時間,將執(zhí)行機會給其他線程癣漆,但是監(jiān)控狀態(tài)依然保持维咸,到時后會自動恢復(線程回到就緒(ready)狀態(tài)),因為調(diào)用 sleep 不會釋放對象鎖惠爽。wait() 是 Object 類的方法癌蓖,對此對象調(diào)用 wait()方法導致本線程放棄對象鎖(線程暫停執(zhí)行),進入等待此對象的等待鎖定池婚肆,只有針對此對象發(fā)出 notify 方法(或 notifyAll)后本線程才進入對象鎖定池準備獲得對象鎖進入就緒狀態(tài)租副。
補充:這里似乎漏掉了一個作為先決條件的問題,就是什么是進程较性,什么是線程用僧?為什么需要多線程編程?答案如下所示:
進程是具有一定獨立功能的程序關(guān)于某個數(shù)據(jù)集合上的一次運行活動赞咙,是操作系統(tǒng)進行資源分配和調(diào)度的一個獨立單位责循;線程是進程的一個實體,是 CPU 調(diào)度和分派的基本單位攀操,是比進程更小的能獨立運行的基本單位院仿。線程的劃分尺度小于進程,這使得多線程程序的并發(fā)性高速和;進程在執(zhí)行時通常擁有獨立的內(nèi)存單元歹垫,而線程之間可以共享內(nèi)存。使用多線程的編程通常能夠帶來更好的性能和用戶體驗健芭,但是多線程的程序?qū)τ谄渌绦蚴遣挥押玫南卦浚驗樗加昧烁嗟?CPU 資源秀姐。
11. sleep() 和 yield() 有什么區(qū)別?
答:
① sleep() 方法給其他線程運行機會時不考慮線程的優(yōu)先級慈迈,因此會給低優(yōu)先級的線程以運行的機會;yield() 方法只會給相同優(yōu)先級或更高優(yōu)先級的線程以運行的機會省有;
② 線程執(zhí)行 sleep() 方法后轉(zhuǎn)入阻塞(blocked)狀態(tài)痒留,而執(zhí)行 yield() 方法后轉(zhuǎn)入就緒(ready)狀態(tài);
③ sleep() 方法聲明拋出InterruptedException蠢沿,而 yield() 方法沒有聲明任何異常伸头;
④ sleep() 方法比 yield() 方法(跟操作系統(tǒng)相關(guān))具有更好的可移植性。
12. 當一個線程進入一個對象的 synchronized 方法 A 之后舷蟀,其它線程是否可進入此對象的 synchronized 方法恤磷?
答:不能面哼。其它線程只能訪問該對象的非同步方法,同步方法則不能進入扫步。
13. 請說出與線程同步相關(guān)的方法魔策。
答:
- wait():使一個線程處于等待(阻塞)狀態(tài),并且釋放所持有的對象的鎖河胎;
- sleep():使一個正在運行的線程處于睡眠狀態(tài)闯袒,是一個靜態(tài)方法,調(diào)用此方法要捕捉InterruptedException 異常游岳;
- notify():喚醒一個處于等待狀態(tài)的線程政敢,當然在調(diào)用此方法的時候,并不能確切的喚醒某一個等待狀態(tài)的線程胚迫,而是由JVM確定喚醒哪個線程喷户,而且與優(yōu)先級無關(guān);
- notityAll():喚醒所有處入等待狀態(tài)的線程访锻,注意并不是給所有喚醒線程一個對象的鎖摩骨,而是讓它們競爭;
JDK 1.5 通過 Lock 接口提供了顯式(explicit)的鎖機制朗若,增強了靈活性以及對線程的協(xié)調(diào)恼五。Lock 接口中定義了加鎖(lock())和解鎖(unlock())的方法,同時還提供了 newCondition() 方法來產(chǎn)生用于線程之間通信的 Condition 對象哭懈;
JDK 1.5 還提供了信號量(semaphore)機制灾馒,信號量可以用來限制對某個共享資源進行訪問的線程的數(shù)量。在對資源進行訪問之前遣总,線程必須得到信號量的許可(調(diào)用Semaphore對象的acquire()方法)睬罗;在完成對資源的訪問后,線程必須向信號量歸還許可(調(diào)用 Semaphore 對象的 release() 方法)旭斥。
14. synchronized 關(guān)鍵字的用法容达?
答:synchronized 關(guān)鍵字可以將對象或者方法標記為同步,以實現(xiàn)對對象和方法的互斥訪問垂券,可以用synchronized(對象) { … }定義同步代碼塊花盐,或者在聲明方法時將 synchronized 作為方法的修飾符。在第60題的例子中已經(jīng)展示了 synchronized 關(guān)鍵字的用法菇爪。
15. 舉例說明同步和異步算芯。
答:如果系統(tǒng)中存在臨界資源(資源數(shù)量少于競爭資源的線程數(shù)量的資源),例如正在寫的數(shù)據(jù)以后可能被另一個線程讀到凳宙,或者正在讀的數(shù)據(jù)可能已經(jīng)被另一個線程寫過了熙揍,那么這些數(shù)據(jù)就必須進行同步存取(數(shù)據(jù)庫操作中的悲觀鎖就是最好的例子)氏涩。當應用程序在對象上調(diào)用了一個需要花費很長時間來執(zhí)行的方法届囚,并且不希望讓程序等待方法的返回時有梆,就應該使用異步編程,在很多情況下采用異步途徑往往更有效率意系。事實上淳梦,所謂的同步就是指阻塞式操作,而異步就是非阻塞式操作昔字。
16. 啟動一個線程是用 run() 還是 start() 方法?
答:啟動一個線程是調(diào)用 start() 方法爆袍,使線程所代表的虛擬處理機處于可運行狀態(tài),這意味著它可以由JVM 調(diào)度并執(zhí)行作郭,這并不意味著線程就會立即運行陨囊。run()方法是線程啟動后要進行回調(diào)(callback)的方法。
17. 什么是線程池(thread pool)夹攒?
答:在面向?qū)ο缶幊讨兄┐祝瑒?chuàng)建和銷毀對象是很費時間的,因為創(chuàng)建一個對象要獲取內(nèi)存資源或者其它更多資源咏尝。在 Java 中更是如此压语,虛擬機將試圖跟蹤每一個對象,以便能夠在對象銷毀后進行垃圾回收编检。所以提高服務程序效率的一個手段就是盡可能減少創(chuàng)建和銷毀對象的次數(shù)胎食,特別是一些很耗資源的對象創(chuàng)建和銷毀,這就是"池化資源"技術(shù)產(chǎn)生的原因允懂。線程池顧名思義就是事先創(chuàng)建若干個可執(zhí)行的線程放入一個池(容器)中厕怜,需要的時候從池中獲取線程不用自行創(chuàng)建,使用完畢不需要銷毀線程而是放回池中蕾总,從而減少創(chuàng)建和銷毀線程對象的開銷粥航。
18. 線程的基本狀態(tài)以及狀態(tài)之間的關(guān)系?
答:
除去起始(new)狀態(tài)和結(jié)束(finished)狀態(tài)生百,線程有三種狀態(tài)递雀,分別是:就緒(ready)、運行(running)和阻塞(blocked)蚀浆。其中就緒狀態(tài)代表線程具備了運行的所有條件缀程,只等待 CPU 調(diào)度(萬事俱備,只欠東風)蜡坊;處于運行狀態(tài)的線程可能因為 CPU 調(diào)度(時間片用完了)的原因回到就緒狀態(tài)杠输,也有可能因為調(diào)用了線程的 yield 方法回到就緒狀態(tài),此時線程不會釋放它占有的資源的鎖秕衙,坐等 CPU 以繼續(xù)執(zhí)行;運行狀態(tài)的線程可能因為 I/O 中斷僵刮、線程休眠据忘、調(diào)用了對象的 wait 方法而進入阻塞狀態(tài)(有的地方也稱之為等待狀態(tài))鹦牛;而進入阻塞狀態(tài)的線程會因為休眠結(jié)束、調(diào)用了對象的 notify 方法或 notifyAll 方法或其他線程執(zhí)行結(jié)束而進入就緒狀態(tài)勇吊。注意:調(diào)用 wait 方法會讓線程進入等待池中等待被喚醒曼追, notify 方法或 notifyAll 方法會讓等待鎖中的線程從等待池進入等鎖池,在沒有得到對象的鎖之前汉规,線程仍然無法獲得 CPU 的調(diào)度和執(zhí)行礼殊。
19. 死鎖的必要條件?怎么克服针史?
答:產(chǎn)生死鎖的四個必要條件:
互斥條件:一個資源每次只能被一個進程使用晶伦。
請求與保持條件:一個進程因請求資源而阻塞時,對已獲得的資源保持不放啄枕。
不剝奪條件:進程已獲得的資源婚陪,在末使用完之前,不能強行剝奪频祝。
循環(huán)等待條件:若干進程之間形成一種頭尾相接的循環(huán)等待資源關(guān)系泌参。
這四個條件是死鎖的必要條件,只要系統(tǒng)發(fā)生死鎖常空,這些條件必然成立沽一,而只要上述條件之一不滿足,就不會發(fā)生死鎖漓糙。
死鎖的解決方法:
a 撤消陷于死鎖的全部進程锯玛;
b 逐個撤消陷于死鎖的進程,直到死鎖不存在兼蜈;
c 從陷于死鎖的進程中逐個強迫放棄所占用的資源攘残,直至死鎖消失。
d 從另外一些進程那里強行剝奪足夠數(shù)量的資源分配給死鎖進程为狸,以解除死鎖狀態(tài)