Java多線程與併發面試題

1,什麼是線程?

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

2,線程和進程有什麼區別?java

線程是進程的子集,一個進程能夠有不少線程,每條線程並行執行不一樣的任務。不一樣的進程使用不一樣的內存空間,而全部的線程共享一片相同的內存空間。每一個線程都擁有單獨的棧內存用來存儲本地數據。程序員

3,如何在Java中實現線程?算法

兩種方式:java.lang.Thread 類的實例就是一個線程可是它須要調用java.lang.Runnable接口來執行,因爲線程類自己就是調用的Runnable接口因此你能夠繼承java.lang.Thread 類或者直接調用Runnable接口來重寫run()方法實現線程。編程

4,Java 關鍵字volatile 與 synchronized 做用與區別?緩存

1,volatile
它所修飾的變量不保留拷貝,直接訪問主內存中的。在Java內存模型中,有main memory,每一個線程也有本身的memory (例如寄存器)。爲了性能,一個線程會在本身的memory中保持要訪問的變量的副本。這樣就會出現同一個變 量在某個瞬間,在一個線程的memory中的值可能與另一個線程memory中的值,或者main memory中的值不一致的狀況。 一個變量聲明爲volatile,就意味着這個變量是隨時會被其餘線程修改的,所以不能將它cache在線程memory中。
2,synchronized安全

當它用來修飾一個方法或者一個代碼塊的時候,可以保證在同一時刻最多隻有一個線程執行該段代碼。數據結構

  1、當兩個併發線程訪問同一個對象object中的這個synchronized(this)同步代碼塊時,一個時間內只能有一個線程獲得執行。另外一個線程必須等待當前線程執行完這個代碼塊之後才能執行該代碼塊。多線程

  2、然而,當一個線程訪問object的一個synchronized(this)同步代碼塊時,另外一個線程仍然能夠訪問該object中的非synchronized(this)同步代碼塊。架構

  3、尤爲關鍵的是,當一個線程訪問object的一個synchronized(this)同步代碼塊時,其餘線程對object中全部其它synchronized(this)同步代碼塊的訪問將被阻塞。

  4、當一個線程訪問object的一個synchronized(this)同步代碼塊時,它就得到了這個object的對象鎖。結果,其它線程對該object對象全部同步代碼部分的訪問都被暫時阻塞。

  5、以上規則對其它對象鎖一樣適用.

5,有哪些不一樣的線程生命週期?

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

6,你對線程優先級的理解是什麼?

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

7,什麼是死鎖(Deadlock)?如何分析和避免死鎖?

死鎖是指兩個以上的線程永遠阻塞的狀況,這種狀況產生至少須要兩個以上的線程和兩個以上的資源。

分析死鎖,咱們須要查看Java應用程序的線程轉儲。咱們須要找出那些狀態爲BLOCKED的線程和他們等待的資源。每一個資源都有一個惟一的id,用這個id咱們能夠找出哪些線程已經擁有了它的對象鎖。

避免嵌套鎖,只在須要的地方使用鎖和避免無限期等待是避免死鎖的一般辦法。

更多詳情查看什麼是死鎖?死鎖發生的四個必要條件是什麼?如何避免和預防死鎖產生?

 

8,什麼是線程安全?Vector是一個線程安全類嗎? 

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

9,Java中如何中止一個線程?

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

10,什麼是ThreadLocal?

ThreadLocal用於建立線程的本地變量,咱們知道一個對象的全部線程會共享它的全局變量,因此這些變量不是線程安全的,咱們可使用同步技術。可是當咱們不想使用同步的時候,咱們能夠選擇ThreadLocal變量。

每一個線程都會擁有他們本身的Thread變量,它們可使用get()\set()方法去獲取他們的默認值或者在線程內部改變他們的值。ThreadLocal實例一般是但願它們同線程狀態關聯起來是private static屬性。

11,Sleep()、suspend()和wait()之間有什麼區別?

Thread.sleep()使當前線程在指定的時間處於「非運行」(Not Runnable)狀態。線程一直持有對象的監視器。好比一個線程當前在一個同步塊或同步方法中,其它線程不能進入該塊或方法中。若是另外一線程調用了interrupt()方法,它將喚醒那個「睡眠的」線程。

注意:sleep()是一個靜態方法。這意味着只對當前線程有效,一個常見的錯誤是調用t.sleep(),(這裏的t是一個不一樣於當前線程的線程)。即使是執行t.sleep(),也是當前線程進入睡眠,而不是t線程。t.suspend()是過期的方法,使用suspend()致使線程進入停滯狀態,該線程會一直持有對象的監視器,suspend()容易引發死鎖問題。

object.wait()使當前線程出於「不可運行」狀態,和sleep()不一樣的是wait是object的方法而不是thread。調用object.wait()時,線程先要獲取這個對象的對象鎖,當前線程必須在鎖對象保持同步,把當前線程添加到等待隊列中,隨後另外一線程能夠同步同一個對象鎖來調用object.notify(),這樣將喚醒原來等待中的線程,而後釋放該鎖。基本上wait()/notify()與sleep()/interrupt()相似,只是前者須要獲取對象鎖。

12,什麼是線程餓死,什麼是活鎖?

當全部線程阻塞,或者因爲須要的資源無效而不能處理,不存在非阻塞線程使資源可用。JavaAPI中線程活鎖可能發生在如下情形:

1,當全部線程在程序中執行Object.wait(0),參數爲0的wait方法。程序將發生活鎖直到在相應的對象上有線程調用Object.notify()或者Object.notifyAll()。

2,當全部線程卡在無限循環中。

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

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

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

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

同步集合與併發集合都爲多線程和併發提供了合適的線程安全的集合,不過併發集合的可擴展性更高。

在Java1.5以前程序員們只有同步集合來用且在多線程併發的時候會致使爭用,阻礙了系統的擴展性。

Java5介紹了併發集合像ConcurrentHashMap,不只提供線程安全還用鎖分離和    內部分區等現代技術提升了可擴展性。

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

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

16,什麼是線程池? 爲何要使用它?

建立線程要花費昂貴的資源和時間,若是任務來了才建立線程那麼響應時間會變長,並且一個進程能建立的線程數有限。

爲了不這些問題,在程序啓動的時候就建立若干線程來響應處理,它們被稱爲線程池,裏面的線程叫工做線程。

從JDK1.5開始,Java API提供了Executor框架讓你能夠建立不一樣的線程池。好比單線程池,每次處理一個任務;數目固定的線程池或者是緩存線程池(一個適合不少生存期短的任務的程序的可擴展線程池)。

17,Java中invokeAndWait 和 invokeLater有什麼區別?

這兩個方法是Swing API 提供給Java開發者用來從當前線程而不是事件派發線程更新GUI組件用的。InvokeAndWait()同步更新GUI組件,好比一個進度條,一旦進度更新了,進度條也要作出相應改變。若是進度被多個線程跟蹤,那麼就調用invokeAndWait()方法請求事件派發線程對組件進行相應更新。而invokeLater()方法是異步調用更新組件的。

18,多線程中的忙循環是什麼?

忙循環就是程序員用循環讓一個線程等待,不像傳統方法wait(), sleep() 或 yield() 它們都放棄了CPU控制,而忙循環不會放棄CPU,它就是在運行一個空循環。這麼作的目的是爲了保留CPU緩存。

在多核系統中,一個等待線程醒來的時候可能會在另外一個內核運行,這樣會重建緩存。爲了不重建緩存和減小等待重建的時間就可使用它了。

19,Java內存模型是什麼?

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

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

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

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

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

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

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

  可傳遞性

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

interrupted() 和 isInterrupted()的主要區別是前者會將中斷狀態清除然後者不會。Java多線程的中斷機制是用內部標識來實現的,調用Thread.interrupt()來中斷一個線程就會設置中斷標識爲true。當中斷線程調用靜態方法Thread.interrupted()來檢查中斷狀態時,中斷狀態會被清零。

非靜態方法isInterrupted()用來查詢其它線程的中斷狀態且不會改變中斷狀態標識。簡單的說就是任何拋出InterruptedException異常的方法都會將中斷狀態清零。不管如何,一個線程的中斷狀態都有可能被其它線程調用中斷來改變。

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

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

無論是同步集合仍是併發集合他們都支持線程安全,他們之間主要的區別體如今性能和可擴展性,還有他們如何實現的線程安全上。

同步HashMap, Hashtable, HashSet, Vector, ArrayList 相比他們併發的實現(ConcurrentHashMap, CopyOnWriteArrayList, CopyOnWriteHashSet)會慢得多。形成如此慢的主要緣由是鎖, 同步集合會把整個Map或List鎖起來,而併發集合不會。併發集合實現線程安全是經過使用先進的和成熟的技術像鎖剝離。

好比ConcurrentHashMap 會把整個Map 劃分紅幾個片斷,只對相關的幾個片斷上鎖,同時容許多線程訪問其餘未上鎖的片斷。

一樣的,CopyOnWriteArrayList 容許多個線程以非同步的方式讀,當有線程寫的時候它會將整個List複製一個副本給它。

若是在讀多寫少這種對併發集合有利的條件下使用併發集合,這會比使用同步集合更具備可伸縮性。

22,什麼是線程池? 爲何要使用它?

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

線程池的做用,就是在調用線程的時候初始化必定數量的線程,有線程過來的時候,先檢測初始化的線程還有空的沒有,沒有就再看當前運行中的線程數是否是已經達到了最大數,若是沒有,就新分配一個線程去處理。

就像餐館中吃飯同樣,從裏面叫一個服務員出來;但若是已經達到了最大數,就至關於服務員已經用盡了,那沒得辦法,另外的線程就只有等了,直到有新的「服務員」爲止。

線程池的優勢就是能夠管理線程,有一個高度中樞,這樣程序纔不會亂,保證系統不會由於大量的併發而由於資源不足掛掉。

23,Java中活鎖和死鎖有什麼區別?

活鎖:一個線程一般會有會響應其餘線程的活動。若是其餘線程也會響應另外一個線程的活動,那麼就有可能發生活鎖。同死鎖同樣,發生活鎖的線程沒法繼續執行。然而線程並無阻塞——他們在忙於響應對方沒法恢復工做。這就至關於兩個在走廊相遇的人:甲向他本身的左邊靠想讓乙過去,而乙向他的右邊靠想讓甲過去。可見他們阻塞了對方。甲向他的右邊靠,而乙向他的左邊靠,他們仍是阻塞了對方。

死鎖:兩個或更多線程阻塞着等待其它處於死鎖狀態的線程所持有的鎖。死鎖一般發生在多個線程同時但以不一樣的順序請求同一組鎖的時候,死鎖會讓你的程序掛起沒法完成任務。

24,如何避免死鎖?

死鎖的發生必須知足如下四個條件:

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

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

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

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

三種用於避免死鎖的技術:

  加鎖順序(線程按照必定的順序加鎖)

  加鎖時限(線程嘗試獲取鎖的時候加上必定的時限,超過期限則放棄對該鎖的請求,並釋放本身佔有的鎖)

  死鎖檢測

25,notify()和notifyAll()有什麼區別?
1,notify()和notifyAll()都是Object對象用於通知處在等待該對象的線程的方法。
2,void notify(): 喚醒一個正在等待該對象的線程。
3,void notifyAll(): 喚醒全部正在等待該對象的線程。
二者的最大區別在於:
  notifyAll使全部原來在該對象上等待被notify的線程通通退出wait的狀態,變成等待該對象上的鎖,一旦該對象被解鎖,他們就會去競爭。

  notify他只是選擇一個wait狀態線程進行通知,並使它得到該對象上的鎖,但不驚動其餘一樣在等待被該對象notify的線程們,當第一個線程運行完畢之後釋放對象上的鎖,此時若是該對象沒有再次使用notify語句,即使該對象已經空閒,其餘wait狀態等待的線程因爲沒有獲得該對象的通知,繼續處在wait狀態,直到這個對象發出一個notify或notifyAll,它們等待的是被notify或notifyAll,而不是鎖。

26,什麼是可重入鎖(ReentrantLock)? 

Java.util.concurrent.lock 中的 Lock 框架是鎖定的一個抽象,它容許把鎖定的實現做爲Java 類,而不是做爲語言的特性來實現。這就爲Lock 的多種實現留下了空間,各類實現可能有不一樣的調度算法、性能特性或者鎖定語義。            ReentrantLock 類實現了Lock ,它擁有與synchronized 相同的併發性和內存語義,可是添加了相似鎖投票、定時鎖等候和可中斷鎖等候的一些特性。此外,它還提供了在激烈爭用狀況下更佳的性能。(換句話說,當許多線程都想訪問共享資源時,JVM能夠花更少的時候來調度線程,把更多時間用在執行線程上。)
Reentrant 鎖意味着什麼呢?簡單來講,它有一個與鎖相關的獲取計數器,若是擁有鎖的某個線程再次獲得鎖,那麼獲取計數器就加1,而後鎖須要被釋放兩次才能得到真正釋放。這模仿了synchronized 的語義;若是線程進入由線程已經擁有的監控器保護的synchronized 塊,就容許線程繼續進行,當線程退出第二個(或者後續)synchronized塊的時候,不釋放鎖,只有線程退出它進入的監控器保護的第一個synchronized 塊時,才釋放鎖。

27,讀寫鎖能夠用於什麼應用場景?
讀寫鎖能夠用於 「多讀少寫」 的場景,讀寫鎖支持多個讀操做併發執行,寫操做只能由一個線程來操做
ReadWriteLock對向數據結構相對不頻繁地寫入,可是有多個任務要常常讀取這個數據結構的這類狀況進行了優化。ReadWriteLock使得你能夠同時有多個讀取者,只要它們都不試圖寫入便可。若是寫鎖已經被其餘任務持有,那麼任何讀取者都不能訪問,直至這個寫鎖被釋放爲止。
ReadWriteLock 對程序性能的提升主要受制於以下幾個因素:
  1,數據被讀取的頻率與被修改的頻率相比較的結果。
  2,讀取和寫入的時間
  3,有多少線程競爭

  4,是否在多處理機器上運行

相關文章
相關標籤/搜索