Java線程面試題

-《Java核心技術 卷一》html

- Java線程面試題 Top 50:http://www.importnew.com/12773.htmljava

- JAVA多線程和併發基礎面試問答: http://ifeve.com/java-multi-threading-concurrency-interview-questions-with-answers/程序員

- 15個頂級Java多線程面試題及回答:http://ifeve.com/15-java-faq/面試

- 其餘博客算法

如有問題,歡迎提出,共同進步!數據庫

1) 什麼是線程?編程

線程是操做系統可以進行運算調度的最小單位,它被包含在進程之中,是進程中的實際運做單位。程序員能夠經過它進行多處理器編程,你可使用多線程對運算密集型任務提速。好比,若是一個線程完成一個任務要100毫秒,那麼用十個線程完成該任務只需10毫秒。數組

2) 線程和進程有什麼區別?緩存

一個進程是一個獨立(self contained)的運行環境,它能夠被看做一個程序或者一個應用。而線程是在進程中執行的一個任務。線程是進程的子集,一個進程能夠有不少線程,每條線程並行執行不一樣的任務。不一樣的進程使用不一樣的內存空間,而全部的線程共享一片相同的內存空間。別把它和棧內存搞混,每一個線程都擁有單獨的棧內存用來存儲本地數據。安全

3) 如何在Java中實現線程?

有兩種建立線程的方法:一是實現Runnable接口,而後將它傳遞給Thread的構造函數,建立一個Thread對象;二是直接繼承Thread類。

4) 用Runnable仍是Thread?

這個問題是上題的後續,你們都知道咱們能夠經過繼承Thread類或者調用Runnable接口來實現線程,問題是,那個方法更好呢?什麼狀況下使用它?這個問題很容易回答,若是你知道Java不支持類的多重繼承,但容許你調用多個接口。因此若是你要繼承其餘類,固然是調用Runnable接口好了。更多詳細信息請點擊這裏。

6) Thread 類中的start() 和 run() 方法有什麼區別?

start()方法被用來啓動新建立的線程,使該被建立的線程狀態變爲可運行狀態。當你調用run()方法的時候,只會是在原來的線程中調用,沒有新的線程啓動,start()方法纔會啓動新線程。若是咱們調用了Thread的run()方法,它的行爲就會和普通的方法同樣,直接運行run()方法。爲了在新的線程中執行咱們的代碼,必須使用Thread.start()方法。

7) Java中Runnable和Callable有什麼不一樣?

Runnable和Callable都表明那些要在不一樣的線程中執行的任務。Runnable從JDK1.0開始就有了,Callable是在JDK1.5增長的。它們的主要區別是Callable的 call() 方法能夠返回值和拋出異常,而Runnable的run()方法沒有這些功能。Callable能夠返回裝載有計算結果的Future對象。

8) Java中CyclicBarrier 和 CountDownLatch有什麼不一樣?

CyclicBarrier 和 CountDownLatch 均可以用來讓一組線程等待其它線程。與 CyclicBarrier 不一樣的是,CountdownLatch 不能從新使用。

9) Java內存模型是什麼?

Java內存模型規定和指引Java程序在不一樣的內存架構、CPU和操做系統間有肯定性地行爲。它在多線程的狀況下尤爲重要。Java內存模型對一個線程所作的變更能被其它線程可見提供了保證,它們之間是先行發生關係。這個關係定義了一些規則讓程序員在併發編程時思路更清晰。好比,先行發生關係確保了:

- 線程內的代碼可以按前後順序執行,這被稱爲程序次序規則。

- 對於同一個鎖,一個解鎖操做必定要發生在時間上後發生的另外一個鎖定操做以前,也叫作管程鎖定規則。

- 前一個對volatile的寫操做在後一個volatile的讀操做以前,也叫volatile變量規則。

- 一個線程內的任何操做必需在這個線程的start()調用以後,也叫做線程啓動規則。

- 一個線程的全部操做都會在線程終止以前,線程終止規則。

- 一個對象的終結操做必需在這個對象構造完成以後,也叫對象終結規則。

- 可傳遞性

強烈建議你們閱讀《Java併發編程實踐》第十六章來加深對Java內存模型的理解。

10) Java中的volatile 變量是什麼?

volatile是一個特殊的修飾符,只有成員變量才能使用它。在Java併發程序缺乏同步類的狀況下,多線程對成員變量的操做對其它線程是透明的。volatile變量能夠保證下一個讀取操做會在前一個寫操做以後發生。線程都會直接從內存中讀取該變量而且不緩存它。這就確保了線程讀取到的變量是同內存中是一致的。

11) 什麼是線程安全?Vector是一個線程安全類嗎?

若是你的代碼所在的進程中有多個線程在同時運行,而這些線程可能會同時運行這段代碼。若是每次運行結果和單線程運行的結果是同樣的,並且其餘的變量的值也和預期的是同樣的,就是線程安全的。一個線程安全的計數器類的同一個實例對象在被多個線程使用的狀況下也不會出現計算失誤。很顯然你能夠將集合類分紅兩組,線程安全和非線程安全的。Vector 是用同步方法來實現線程安全的, 而和它類似的ArrayList不是線程安全的。

12) Java中什麼是競態條件?

在大多數實際的多線程應用中,兩個或兩個以上的線程須要共享對同一數據的存取。若是i線程存取相同的對象,而且每個線程都調用了一個修改該對象狀態的方法,將會發生什麼呢?能夠想象,線程彼此踩了對方的腳。根據線程訪問數據的次序,可能會產生訛誤的對象。這樣的狀況一般稱爲競爭條件。

13) Java中如何中止一個線程?

Java提供了很豐富的API但沒有爲中止線程提供API。JDK 1.0原本有一些像stop(), suspend() 和 resume()的控制方法,可是因爲潛在的死鎖威脅。所以在後續的JDK版本中他們被棄用了,以後Java API的設計者就沒有提供一個兼容且線程安全的方法來中止一個線程。當run() 或者 call() 方法執行完的時候線程會自動結束,若是要手動結束一個線程,能夠用volatile 布爾變量來退出run()方法的循環或者是取消任務來中斷線程。

14) 一個線程運行時發生異常會怎樣?

若是異常沒有被捕獲該線程將會中止執行。Thread.UncaughtExceptionHandler是用於處理未捕獲異常形成線程忽然中斷狀況的一個內嵌接口。當一個未捕獲異常將形成線程中斷的時候JVM會使用Thread.getUncaughtExceptionHandler()來查詢線程的UncaughtExceptionHandler並將線程和異常做爲參數傳遞給handler的uncaughtException()方法進行處理。

15) 如何在兩個線程間共享數據?

你能夠經過共享對象來實現這個目的,或者是使用像阻塞隊列這樣併發的數據結構。這篇教程《Java線程間通訊》(涉及到在兩個線程間共享對象)用wait和notify方法實現了生產者消費者模型。

16) Java中notify 和 notifyAll有什麼區別?

這又是一個刁鑽的問題,由於多線程能夠等待單監控鎖,Java API 的設計人員提供了一些方法當等待條件改變的時候通知它們,可是這些方法沒有徹底實現。notify()方法不能喚醒某個具體的線程,因此只有一個線程在等待的時候它纔有用武之地。而notifyAll()喚醒全部線程並容許他們爭奪鎖確保了至少有一個線程能繼續運行。

17) 爲何wait, notify 和 notifyAll這些方法不在thread類裏面?

一個很明顯的緣由是JAVA提供的鎖是對象級的而不是線程級的,每一個對象都有鎖,經過線程得到。若是線程須要等待某些鎖那麼調用對象中的wait()方法就有意義了。若是wait()方法定義在Thread類中,線程正在等待的是哪一個鎖就不明顯了。簡單的說,因爲wait,notify和notifyAll都是鎖級別的操做,因此把他們定義在Object類中由於鎖屬於對象。

18) 什麼是ThreadLocal變量?

ThreadLocal是Java裏一種特殊的變量。每一個線程都有一個ThreadLocal就是每一個線程都擁有了本身獨立的一個變量,競爭條件被完全消除了。若是爲每一個線程提供一個本身獨有的變量拷貝,將大大提升效率。首先,經過複用減小了代價高昂的對象的建立個數。其次,你在沒有使用高代價的同步或者不變性的狀況下得到了線程安全。

19) 什麼是FutureTask?

在Java併發程序中FutureTask表示一個能夠取消的異步運算。它有啓動和取消運算、查詢運算是否完成和取回運算結果等方法。只有當運算完成的時候結果才能取回,若是運算還沒有完成get方法將會阻塞。一個FutureTask對象能夠對調用了Callable和Runnable的對象進行包裝,因爲FutureTask也是調用了Runnable接口因此它能夠提交給Executor來執行。

20) Java中interrupted 和 isInterruptedd方法的區別?

interrupted() 和 isInterrupted()的主要區別是前者會將中斷狀態清除然後者不會。Java多線程的中斷機制是用內部標識來實現的,調用Thread.interrupt()來中斷一個線程就會設置中斷標識爲true。當中斷線程調用靜態方法Thread.interrupted()來檢查中斷狀態時,中斷狀態會被清零。而非靜態方法isInterrupted()用來查詢其它線程的中斷狀態且不會改變中斷狀態標識。簡單的說就是任何拋出InterruptedException異常的方法都會將中斷狀態清零。不管如何,一個線程的中斷狀態有有可能被其它線程調用中斷來改變。

21) 爲何wait和notify方法要在同步塊中調用?

當一個線程須要調用對象的wait()方法的時候,這個線程必須擁有該對象的鎖,接着它就會釋放這個對象鎖並進入等待狀態直到其餘線程調用這個對象上的notify()方法。一樣的,當一個線程須要調用對象的notify()方法時,它會釋放這個對象的鎖,以便其餘在等待的線程就能夠獲得這個對象鎖。因爲全部的這些方法都須要線程持有對象的鎖,這樣就只能經過同步來實現,因此他們只能在同步方法或者同步塊中被調用。若是你不這麼作,代碼會拋出IllegalMonitorStateException異常。

22) 爲何你應該在循環中檢查等待條件?

處於等待狀態的線程可能會收到錯誤警報和僞喚醒,若是不在循環中檢查等待條件,程序就會在沒有知足結束條件的狀況下退出。所以,當一個等待線程醒來時,不能認爲它原來的等待狀態仍然是有效的,在notify()方法調用以後和等待線程醒來以前這段時間它可能會改變。這就是在循環中使用wait()方法效果更好的緣由,你能夠在Eclipse中建立模板調用wait和notify試一試。若是你想了解更多關於這個問題的內容,推薦你閱讀《Effective Java》這本書中的線程和同步章節。

23) Java中的同步集合與併發集合有什麼區別?

同步集合與併發集合都爲多線程和併發提供了合適的線程安全的集合,不過併發集合的可擴展性更高。在Java1.5以前程序員們只有同步集合來用且在多線程併發的時候會致使爭用,阻礙了系統的擴展性。Java5介紹了併發集合像ConcurrentHashMap,不只提供線程安全還用鎖分離和內部分區等現代技術提升了可擴展性。更多內容詳見答案。

24) Java中堆和棧有什麼不一樣?

爲何把這個問題歸類在多線程和併發面試題裏?由於棧是一塊和線程緊密相關的內存區域。每一個線程都有本身的棧內存,用於存儲本地變量,方法參數和棧調用,一個線程中存儲的變量對其它線程是不可見的。而堆是全部線程共享的一片公用內存區域。對象都在堆裏建立,爲了提高效率線程會從堆中弄一個緩存到本身的棧,若是多個線程使用該變量就可能引起問題,這時volatile 變量就能夠發揮做用了,它要求線程從主存中讀取變量的值。

更多內容詳見答案。

25) 什麼是線程池? 爲何要使用它?

建立線程要花費昂貴的資源和時間,若是任務來了才建立線程那麼響應時間會變長,並且一個進程能建立的線程數有限。爲了不這些問題,在程序啓動的時候就建立若干線程來響應處理,它們被稱爲線程池,裏面的線程叫工做線程。從JDK1.5開始,Java API提供了Executor框架讓你能夠建立不一樣的線程池。好比單線程池,每次處理一個任務;數目固定的線程池或者是緩存線程池(一個適合不少生存期短的任務的程序的可擴展線程池)。

26) 如何寫代碼來解決生產者消費者問題?

在現實中你解決的許多線程問題都屬於生產者消費者模型,就是一個線程生產任務供其它線程進行消費,你必須知道怎麼進行線程間通訊來解決這個問題。比較低級的辦法是用wait和notify來解決這個問題,比較讚的辦法是用Semaphore 或者 BlockingQueue來實現生產者消費者模型。

27) 如何避免死鎖?

Java多線程中的死鎖

死鎖是指兩個或兩個以上的進程在執行過程當中,因爭奪資源而形成的一種互相等待的現象,若無外力做用,它們都將沒法推動下去。這是一個嚴重的問題,由於死鎖會讓你的程序掛起沒法完成任務,死鎖的發生必須知足如下四個條件:

- 互斥條件:一個資源每次只能被一個進程使用。

- 請求與保持條件:一個進程因請求資源而阻塞時,對已得到的資源保持不放。

- 不剝奪條件:進程已得到的資源,在末使用完以前,不能強行剝奪。

- 循環等待條件:若干進程之間造成一種頭尾相接的循環等待資源關係。

避免死鎖最簡單的方法就是阻止循環等待條件,將系統中全部的資源設置標誌位、排序,規定全部的進程申請資源必須以必定的順序(升序或降序)作操做來避免死鎖。

28) Java中活鎖和死鎖有什麼區別?

這是上題的擴展,活鎖和死鎖相似,不一樣之處在於處於活鎖的線程或進程的狀態是不斷改變的,活鎖能夠認爲是一種特殊的飢餓。一個現實的活鎖例子是兩我的在狹小的走廊碰到,兩我的都試着避讓對方好讓彼此經過,可是由於避讓的方向都同樣致使最後誰都不能經過走廊。簡單的說就是,活鎖和死鎖的主要區別是前者進程的狀態能夠改變可是卻不能繼續執行。

29) 怎麼檢測一個線程是否擁有鎖?

在java.lang.Thread中有一個方法叫holdsLock(),它返回true若是當且僅當當前線程擁有某個具體對象的鎖。

30) 你如何在Java中獲取線程堆棧?

對於不一樣的操做系統,有多種方法來得到Java進程的線程堆棧。當你獲取線程堆棧時,JVM會把全部線程的狀態存到日誌文件或者輸出到控制檯。在Windows你可使用Ctrl + Break組合鍵來獲取線程堆棧,Linux下用kill -3命令。你也能夠用jstack這個工具來獲取,它對線程id進行操做,你能夠用jps這個工具找到id。

31) JVM中哪一個參數是用來控制線程的棧堆棧小的

這個問題很簡單, -Xss參數用來控制線程的堆棧大小。你能夠查看JVM配置列表來了解這個參數的更多信息。

32) Java中synchronized 和 ReentrantLock 有什麼不一樣?

Java在過去很長一段時間只能經過synchronized關鍵字來實現互斥,它有一些缺點。好比你不能擴展鎖以外的方法或者塊邊界,嘗試獲取鎖時不能中途取消等。Java 5 經過Lock接口提供了更復雜的控制來解決這些問題。 ReentrantLock 類實現了 Lock,它擁有與 synchronized 相同的併發性和內存語義且它還具備可擴展性。

33) 有三個線程T1,T2,T3,怎麼確保它們按順序執行(確保main()方法所在的線程是Java程序最後結束的線程)?

在多線程中有多種方法讓線程按特定順序執行,你能夠用線程類的join()方法在一個線程中啓動另外一個線程,另一個線程完成該線程繼續執行。爲了確保三個線程的順序你應該先啓動最後一個(T3調用T2,T2調用T1),這樣T1就會先完成而T3最後完成。

34) Thread類中的yield方法有什麼做用?

yield方法能夠暫停當前正在執行的線程對象,讓其它有相同優先級的線程執行。它是一個靜態方法並且只保證當前線程放棄CPU佔用而不能保證使其它線程必定能佔用CPU,執行yield()的線程有可能在進入到暫停狀態後立刻又被執行。點擊這裏查看更多yield方法的相關內容。

35) Java中ConcurrentHashMap的併發度是什麼?

ConcurrentHashMap把實際map劃分紅若干部分來實現它的可擴展性和線程安全。這種劃分是使用併發度得到的,它是ConcurrentHashMap類構造函數的一個可選參數,默認值爲16,這樣在多線程狀況下就能避免爭用。

36) Java中Semaphore是什麼?

Java中的Semaphore是一種新的同步類,它是一個計數信號。從概念上講,從概念上講,信號量維護了一個許可集合。若有必要,在許可可用前會阻塞每個 acquire(),而後再獲取該許可。每一個 release()添加一個許可,從而可能釋放一個正在阻塞的獲取者。可是,不使用實際的許可對象,Semaphore只對可用許可的號碼進行計數,並採起相應的行動。信號量經常用於多線程的代碼中,好比數據庫鏈接池。更多詳細信息請點擊這裏。

37)若是你提交任務時,線程池隊列已滿。會時發會生什麼?

這個問題問得很狡猾,許多程序員會認爲該任務會阻塞直到線程池隊列有空位。事實上若是一個任務不能被調度執行那麼ThreadPoolExecutor’s submit()方法將會拋出一個RejectedExecutionException異常。

38) Java線程池中submit() 和 execute()方法有什麼區別?

兩個方法均可以向線程池提交任務,execute()方法的返回類型是void,它定義在Executor接口中, 而submit()方法能夠返回持有計算結果的Future對象,它定義在ExecutorService接口中,它擴展了Executor接口,其它線程池類像ThreadPoolExecutor和ScheduledThreadPoolExecutor都有這些方法。更多詳細信息請點擊這裏。

39) 什麼是阻塞式方法?

阻塞式方法是指程序會一直等待該方法完成期間不作其餘事情,ServerSocket的accept()方法就是一直等待客戶端鏈接。這裏的阻塞是指調用結果返回以前,當前線程會被掛起,直到獲得結果以後纔會返回。此外,還有異步和非阻塞式方法在任務完成前就返回。更多詳細信息請點擊這裏。

40) 你對線程優先級的理解是什麼?

每個線程都是有優先級的,通常來講,高優先級的線程在運行時會具備優先權,但這依賴於線程調度的實現,這個實現是和操做系統相關的(OS dependent)。咱們能夠定義線程的優先級,可是這並不能保證高優先級的線程會在低優先級的線程前執行。線程優先級是一個int變量(從1-10),1表明最低優先級,10表明最高優先級。

41) 什麼是線程調度器(Thread Scheduler)和時間分片(Time Slicing)?

線程調度器是一個操做系統服務,它負責爲Runnable狀態的線程分配CPU時間。一旦咱們建立一個線程並啓動它,它的執行便依賴於線程調度器的實現。時間分片是指將可用的CPU時間分配給可用的Runnable線程的過程。分配CPU時間能夠基於線程優先級或者線程等待的時間。線程調度並不受到Java虛擬機控制,因此由應用程序來控制它是更好的選擇(也就是說不要讓你的程序依賴於線程的優先級)。

42) 在多線程中,什麼是上下文切換(context-switching)?

上下文切換是存儲和恢復CPU狀態的過程,它使得線程執行可以從中斷點恢復執行。上下文切換是多任務操做系統和多線程環境的基本特徵。

43) 如何在Java中建立Immutable對象?

Immutable對象能夠在沒有同步的狀況下共享,下降了對該對象進行併發訪問時的同步化開銷。要建立不可變類,要實現下面幾個步驟:經過構造方法初始化全部成員、對變量不要提供setter方法、將全部的成員聲明爲私有的,這樣就不容許直接訪問這些成員、在getter方法中,不要直接返回對象自己,而是克隆對象,並返回對象的拷貝。

44) Java中的ReadWriteLock是什麼?

通常而言,讀寫鎖是用來提高併發程序性能的鎖分離技術的成果。Java中的ReadWriteLock是Java 5 中新增的一個接口,一個ReadWriteLock維護一對關聯的鎖,一個用於只讀操做一個用於寫。在沒有寫線程的狀況下一個讀鎖可能會同時被多個讀線程持有。寫鎖是獨佔的,你可使用JDK中的ReentrantReadWriteLock來實現這個規則,它最多支持65535個寫鎖和65535個讀鎖。

45) 多線程中的忙循環是什麼?

忙循環就是程序員用循環讓一個線程等待,不像傳統方法wait(), sleep() 或 yield() 它們都放棄了CPU控制,而忙循環不會放棄CPU,它就是在運行一個空循環。這麼作的目的是爲了保留CPU緩存,在多核系統中,一個等待線程醒來的時候可能會在另外一個內核運行,這樣會重建緩存。爲了不重建緩存和減小等待重建的時間就可使用它了。

46)volatile 變量和 atomic 變量有什麼不一樣?

這是個有趣的問題。首先,volatile 變量和 atomic 變量看起來很像,但功能卻不同。Volatile變量能夠確保先行關係,即寫操做會發生在後續的讀操做以前, 但它並不能保證原子性。例如用volatile修飾count變量那麼 count++ 操做就不是原子性的。而AtomicInteger類提供的atomic方法可讓這種操做具備原子性如getAndIncrement()方法會原子性的進行增量操做把當前值加一,其它數據類型和引用變量也能夠進行類似操做。

47) 若是同步塊內的線程拋出異常會發生什麼?

這個問題坑了不少Java程序員,若你能想到鎖是否釋放這條線索來回答還有點但願答對。不管你的同步塊是正常仍是異常退出的,裏面的線程都會釋放鎖,因此對比鎖接口咱們更喜歡同步塊,由於它不用花費精力去釋放鎖,該功能能夠在finally block裏釋放鎖實現。

48) 單例模式的雙檢鎖是什麼?

這個問題在Java面試中常常被問到,可是面試官對回答此問題的滿意度僅爲50%。一半的人寫不出雙檢鎖還有一半的人說不出它的隱患和Java1.5是如何對它修正的。它實際上是一個用來建立線程安全的單例的老方法,當單例實例第一次被建立時它試圖用單個鎖進行性能優化,可是因爲太過於複雜在JDK1.4中它是失敗的。

49) 如何在Java中建立線程安全的Singleton?

這是上面那個問題的後續,若是你不喜歡雙檢鎖而面試官問了建立Singleton類的替代方法,你能夠利用JVM的類加載和靜態變量初始化特徵來建立Singleton實例,或者是利用枚舉類型來建立Singleton。

50) 寫出3條你遵循的多線程最佳實踐

如下三條最佳實踐大多數Java程序員都應該遵循:

- 給你的線程起個有意義的名字。

這樣能夠方便找bug或追蹤。OrderProcessor, QuoteProcessor or TradeProcessor 這種名字比 Thread-1. Thread-2 and Thread-3 好多了,給線程起一個和它要完成的任務相關的名字,全部的主要框架甚至JDK都遵循這個最佳實踐。

- 避免鎖定和縮小同步的範圍

鎖花費的代價高昂且上下文切換更耗費時間空間,試試最低限度的使用同步和鎖,縮小臨界區。所以相對於同步方法我更喜歡同步塊,它給我擁有對鎖的絕對控制權。

- 多用同步類少用wait 和 notify

首先,CountDownLatch, Semaphore, CyclicBarrier 和 Exchanger 這些同步類簡化了編碼操做,而用wait和notify很難實現對複雜控制流的控制。其次,這些類是由最好的企業編寫和維護在後續的JDK中它們還會不斷優化和完善,使用這些更高等級的同步工具你的程序能夠不費吹灰之力得到優化。

- 多用併發集合少用同步集合

這是另一個容易遵循且受益巨大的最佳實踐,併發集合比同步集合的可擴展性更好,因此在併發編程時使用併發集合效果更好。若是下一次你須要用到map,你應該首先想到用ConcurrentHashMap。

51) 如何強制啓動一個線程?

這個問題就像是如何強制進行Java垃圾回收,目前尚未以爲方法,雖然你可使用System.gc()來進行垃圾回收,可是不保證能成功。在Java裏面沒有辦法強制啓動一個線程,它是被線程調度器控制着且Java沒有公佈相關的API。

52) Java中的fork join框架是什麼?

fork join框架是JDK7中出現的一款高效的工具,Java開發人員能夠經過它充分利用現代服務器上的多處理器。它是專門爲了那些能夠遞歸劃分紅許多子模塊設計的,目的是將全部可用的處理能力用來提高程序的性能。fork join框架一個巨大的優點是它使用了工做竊取算法,能夠完成更多任務的工做線程能夠從其它線程中竊取任務來執行。

53) Java多線程中調用wait() 和 sleep()方法有什麼不一樣?

Java程序中wait 和 sleep都會形成某種形式的暫停,它們能夠知足不一樣的須要。wait()方法用於線程間通訊,若是等待條件爲真且其它線程被喚醒時它會釋放鎖,而sleep()方法僅僅釋放CPU資源或者讓當前線程中止執行一段時間,但不會釋放鎖。須要注意的是,sleep()並不會讓線程終止,一旦從休眠中喚醒線程,線程的狀態將會被改變爲Runnable,而且根據線程調度,它將獲得執行。

54) 什麼是Thread Group?爲何不建議使用它?

ThreadGroup是一個類,它的目的是提供關於線程組的信息。

ThreadGroup API比較薄弱,它並無比Thread提供了更多的功能。它有兩個主要的功能:一是獲取線程組中處於活躍狀態線程的列表;二是設置爲線程設置未捕獲異常處理器(ncaught exception handler)。但在Java 1.5中Thread類也添加了setUncaughtExceptionHandler(UncaughtExceptionHandler eh) 方法,因此ThreadGroup是已通過時的,不建議繼續使用。

55) 什麼是Java線程轉儲(Thread Dump),如何獲得它?

線程轉儲是一個JVM活動線程的列表,它對於分析系統瓶頸和死鎖很是有用。有不少方法能夠獲取線程轉儲——使用Profiler,Kill -3命令,jstack工具等等。咱們更喜歡jstack工具,由於它容易使用而且是JDK自帶的。因爲它是一個基於終端的工具,因此咱們能夠編寫一些腳本去定時的產生線程轉儲以待分析。

56) 什麼是Java Timer類?如何建立一個有特定時間間隔的任務?

java.util.Timer是一個工具類,能夠用於安排一個線程在將來的某個特定時間執行。Timer類能夠用安排一次性任務或者週期任務。

java.util.TimerTask是一個實現了Runnable接口的抽象類,咱們須要去繼承這個類來建立咱們本身的定時任務並使用Timer去安排它的執行。

57) 什麼是原子操做?在Java Concurrency API中有哪些原子類(atomic classes)?

原子操做是指一個不受其餘操做影響的操做任務單元。原子操做是在多線程環境下避免數據不一致必須的手段。

int++並非一個原子操做,因此當一個線程讀取它的值並加1時,另一個線程有可能會讀到以前的值,這就會引起錯誤。

在 java.util.concurrent.atomic 包中添加原子變量類以後,這種狀況才發生了改變。全部原子變量類都公開比較並設置原語(與比較並交換相似),這些原語都是使用平臺上可用的最快本機結構(比較並交換、加載連接/條件存儲,最壞的狀況下是旋轉鎖)來實現的。 java.util.concurrent.atomic 包中提供了原子變量的 9 種風格( AtomicInteger; AtomicLong; AtomicReference; AtomicBoolean;原子整型;長型;引用;及原子標記引用和戳記引用類的數組形式,其原子地更新一對值)。

58) Java Concurrency API中的Lock接口(Lock interface)是什麼?對比同步它有什麼優點?

Lock接口比同步方法和同步塊提供了更具擴展性的鎖操做。他們容許更靈活的結構,能夠具備徹底不一樣的性質,而且能夠支持多個相關類的條件對象。

它的優點有:

- 可使鎖更公平

- 可使線程在等待鎖的時候響應中斷

- 可讓線程嘗試獲取鎖,並在沒法獲取鎖的時候當即返回或者等待一段時間

- 能夠在不一樣的範圍,以不一樣的順序獲取和釋放鎖

59) 什麼是Executor框架?

Executor框架同java.util.concurrent.Executor 接口在Java 5中被引入。Executor框架是一個根據一組執行策略調用,調度,執行和控制的異步任務的框架。

無限制的建立線程會引發應用程序內存溢出。因此建立一個線程池是個更好的的解決方案,由於能夠限制線程的數量而且能夠回收再利用這些線程。利用Executor框架能夠很是方便的建立一個線程池。

60) Executors類是什麼?

Executors爲Executor,ExecutorService,ScheduledExecutorService,ThreadFactory和Callable類提供了一些工具方法。

Executors能夠用於方便的建立線程池。

61) 什麼是阻塞隊列?如何使用阻塞隊列來實現生產者-消費者模型?

java.util.concurrent.BlockingQueue的特性是:當隊列是空的時,從隊列中獲取或刪除元素的操做將會被阻塞,或者當隊列是滿時,往隊列裏添加元素的操做會被阻塞。

阻塞隊列不接受空值,當你嘗試向隊列中添加空值的時候,它會拋出NullPointerException。

阻塞隊列的實現都是線程安全的,全部的查詢方法都是原子的而且使用了內部鎖或者其餘形式的併發控制。

BlockingQueue 接口是java collections框架的一部分,它主要用於實現生產者-消費者問題。

62)什麼是Callable和Future?

Java 5在concurrency包中引入了java.util.concurrent.Callable 接口,它和Runnable接口很類似,但它能夠返回一個對象或者拋出一個異常。

Callable接口使用泛型去定義它的返回類型。Executors類提供了一些有用的方法去在線程池中執行Callable內的任務。因爲Callable任務是並行的,咱們必須等待它返回的結果。java.util.concurrent.Future對象爲咱們解決了這個問題。在線程池提交Callable任務後返回了一個Future對象,使用它咱們能夠知道Callable任務的狀態和獲得Callable返回的執行結果。Future提供了get()方法讓咱們能夠等待Callable結束並獲取它的執行結果。

63) 什麼是FutureTask?

FutureTask包裝器是一種很是便利的機制,可將Callable轉換成Future和Runnable,它同時實現二者的接口。

FutureTask類是Future 的一個實現,並實現了Runnable,因此可經過Excutor(線程池) 來執行。也可傳遞給Thread對象執行。若是在主線程中須要執行比較耗時的操做時,但又不想阻塞主線程時,能夠把這些做業交給Future對象在後臺完成,當主線程未來須要時,就能夠經過Future對象得到後臺做業的計算結果或者執行狀態。

64) 什麼是併發容器的實現?

Java集合類都是快速失敗的,這就意味着當集合被改變且一個線程在使用迭代器遍歷集合的時候,迭代器的next()方法將拋出ConcurrentModificationException異常。

併發容器:併發容器是針對多個線程併發訪問設計的,在jdk5.0引入了concurrent包,其中提供了不少併發容器,如ConcurrentHashMap,CopyOnWriteArrayList等。併發容器使用了與同步容器徹底不一樣的加鎖策略來提供更高的併發性和伸縮性,例如在ConcurrentHashMap中採用了一種粒度更細的加鎖機制,能夠稱爲分段鎖,在這種鎖機制下,容許任意數量的讀線程併發地訪問map,而且執行讀操做的線程和寫操做的線程也能夠併發的訪問map,同時容許必定數量的寫操做線程併發地修改map,因此它能夠在併發環境下實現更高的吞吐量。

65)用戶線程和守護線程有什麼區別?

當咱們在Java程序中建立一個線程,它就被稱爲用戶線程。一個守護線程是在後臺執行而且不會阻止JVM終止的線程。當沒有用戶線程在運行的時候,JVM關閉程序而且退出。一個守護線程建立的子線程依然是守護線程。

66)有哪些不一樣的線程生命週期?

當咱們在Java程序中新建一個線程時,它的狀態是New。當咱們調用線程的start()方法時,狀態被改變爲Runnable。線程調度器會爲Runnable線程池中的線程分配CPU時間而且講它們的狀態改變爲Running。其餘的線程狀態還有Waiting,Blocked 和Dead。

67)線程之間是如何通訊的?

當線程間是能夠共享資源時,線程間通訊是協調它們的重要的手段。Object類中wait()\notify()\notifyAll()方法能夠用於線程間通訊關於資源的鎖的狀態。

68)爲何Thread類的sleep()和yield()方法是靜態的?

Thread類的sleep()和yield()方法將在當前正在執行的線程上運行。因此在其餘處於等待狀態的線程上調用這些方法是沒有意義的。這就是爲何這些方法是靜態的。它們能夠在當前正在執行的線程中工做,並避免程序員錯誤的認爲能夠在其餘非運行線程調用這些方法。

69)如何確保線程安全?

在Java中能夠有不少方法來保證線程安全——同步,使用原子類(atomic concurrent classes),實現併發鎖,使用volatile關鍵字,使用不變類和線程安全類。

70)同步方法和同步塊,哪一個是更好的選擇?

同步塊是更好的選擇,由於它不會鎖住整個對象(固然你也可讓它鎖住整個對象)。同步方法會鎖住整個對象,哪怕這個類中有多個不相關聯的同步塊,這一般會致使他們中止執行並須要等待得到這個對象上的鎖。

71)如何建立守護線程?

使用Thread類的setDaemon(true)方法能夠將線程設置爲守護線程,須要注意的是,須要在調用start()方法前調用這個方法,不然會拋出IllegalThreadStateException異常。

72)線程調度策略?

(1) 搶佔式調度策略

Java運行時系統的線程調度算法是搶佔式的 (preemptive)。Java運行時系統支持一種簡單的固定優先級的調度算法。若是一個優先級比其餘任何處於可運行狀態的線程都高的線程進入就緒狀態,那麼運行時系統就會選擇該線程運行。新的優先級較高的線程搶佔(preempt)了其餘線程。可是Java運行時系統並不搶佔同優先級的線程。換句話說,Java運行時系統不是分時的(time-slice)。然而,基於Java Thread類的實現系統多是支持分時的,所以編寫代碼時不要依賴分時。當系統中的處於就緒狀態的線程都具備相同優先級時,線程調度程序採用一種簡單的、非搶佔式的輪轉的調度順序。

(2) 時間片輪轉調度策略

有些系統的線程調度採用時間片輪轉(round-robin)調度策略。這種調度策略是從全部處於就緒狀態的線程中選擇優先級最高的線程分配必定的CPU時間運行。該時間事後再選擇其餘線程運行。只有當線程運行結束、放棄(yield)CPU或因爲某種緣由進入阻塞狀態,低優先級的線程纔有機會執行。若是有兩個優先級相同的線程都在等待CPU,則調度程序以輪轉的方式選擇運行的線程。

73) 在線程中你怎麼處理不可捕捉異常?

Thread.UncaughtExceptionHandler是java SE5中的新接口,它容許咱們在每個Thread對象上添加一個異常處理器。

相關文章
相關標籤/搜索