最近開始準備秋招了,今天來總結一下面試中常常遇到有關Java併發的面試題,若有不當請多指教!java
什麼是線程和進程?
請簡要描述線程與進程的關係,區別及優缺點?
說說併發與並行的區別?
說說線程的生命週期和狀態?
什麼是上下文切換?
什麼是線程死鎖?如何避免死鎖?
說說 sleep() 方法和 wait() 方法區別和共同點?
爲何咱們調用 start() 方法時會執行 run() 方法,爲何咱們不能直接調用 run() 方法?
- 進程就是在運行過程當中的程序,就好像手機運行中的微信,QQ,這些就叫作進程。
- 線程就是進程的執行單元,就好像一個音樂軟件能夠聽音樂,下載音樂,這些任務都是由線程來完成的。
線程與進程類似,但線程是一個比進程更小的執行單位。一個進程在其執行的過程當中能夠產生多個線程。與進程不一樣的是同類的多個線程共享進程的堆和方法區資源,但每一個線程有本身的程序計數器、虛擬機棧和本地方法棧,因此係統在產生一個線程,或是在各個線程之間做切換工做時,負擔要比進程小得多,也正由於如此,線程也被稱爲輕量級進程。git
從JVM角度說進程和線程之間的關係
從上圖能夠看出:一個進程中能夠有多個線程,多個線程共享進程的堆和方法區 (JDK1.8 以後的元空間)資源,可是每一個線程有本身的程序計數器、虛擬機棧 和本地方法棧。github
擴展知識:爲何程序計數器、虛擬機棧和本地方法棧是線程私有的呢?爲何堆和方法區是線程共享的呢?面試
程序計數器爲何是私有的?
程序計數器主要有下面兩個做用:編程
- 字節碼解釋器經過改變程序計數器來依次讀取指令,從而實現代碼的流程控制,如:順序執行、選擇、循環、異常處理。
- 在多線程的狀況下,程序計數器用於記錄當前線程執行的位置,從而當線程被切換回來的時候可以知道該線程上次運行到哪兒了。
因此,程序計數器私有主要是爲了線程切換後能恢復到正確的執行位置。微信
虛擬機棧和本地方法棧爲何是私有的?
因此,爲了保證線程中的局部變量不被別的線程訪問到,虛擬機棧和本地方法棧是線程私有的。多線程
堆和方法區
堆和方法區是全部線程共享的資源,其中堆是進程中最大的一塊內存,主要用於存放新建立的對象 (全部對象都在這裏分配內存),方法區主要用於存放已被加載的類信息、常量、靜態變量、即時編譯器編譯後的代碼等數據。併發
Java 線程在運行的生命週期中的指定時刻只可能處於下面 6 種不一樣狀態的其中一個狀態ide
線程建立以後它將處於 NEW(新建) 狀態,調用 start()
方法後開始運行,線程這時候處於 READY(可運行) 狀態。可運行狀態的線程得到了 CPU 時間片(timeslice)後就處於 RUNNING(運行) 狀態。
當線程執行 wait()
方法以後,線程進入 WAITING(等待) 狀態。進入等待狀態的線程須要依靠其餘線程的通知纔可以返回到運行狀態,而 TIME_WAITING(超時等待) 狀態至關於在等待狀態的基礎上增長了超時限制,好比經過 sleep(long millis)
方法或 wait(long millis)
方法能夠將 Java 線程置於 TIMED WAITING 狀態。當超時時間到達後 Java 線程將會返回到 RUNNABLE 狀態。當線程調用同步方法時,在沒有獲取到鎖的狀況下,線程將會進入到 BLOCKED(阻塞) 狀態。線程在執行 Runnable 的run()
方法以後將會進入到 TERMINATED(終止) 狀態。ui
多線程編程中通常線程的個數都大於 CPU 核心的個數,而一個 CPU 核心在任意時刻只能被一個線程使用,爲了讓這些線程都能獲得有效執行,CPU 採起的策略是爲每一個線程分配時間片並輪轉的形式。當一個線程的時間片用完的時候就會從新處於就緒狀態讓給其餘線程使用,這個過程就屬於一次上下文切換。
什麼是死鎖
多個線程同時被阻塞,它們中的一個或者所有都在等待某個資源被釋放。因爲線程被無限期地阻塞,所以程序不可能正常終止。
舉個例子:線程 A 持有資源 2,線程 B 持有資源 1,他們同時都想申請對方的資源,因此這兩個線程就會互相等待而進入死鎖狀態。
產生死鎖的條件
產生死鎖必須具有如下四個條件:
- 互斥條件:該資源任意一個時刻只由一個線程佔用。
- 請求與保持條件:一個進程因請求資源而阻塞時,對已得到的資源保持不放。
- 不剝奪條件:線程已得到的資源在末使用完以前不能被其餘線程強行剝奪,只有本身使用完畢後才釋放資源。
- 循環等待條件:若干進程之間造成一種頭尾相接的循環等待資源關係。
如何避免線程死鎖?
咱們只要破壞產生死鎖的四個條件中的其中一個就能夠了。
破壞互斥條件
這個條件咱們沒有辦法破壞,由於咱們用鎖原本就是想讓他們互斥的(臨界資源須要互斥訪問)。
破壞請求與保持條件
一次性申請全部的資源。
破壞不剝奪條件
佔用部分資源的線程進一步申請其餘資源時,若是申請不到,能夠主動釋放它佔有的資源。
破壞循環等待條件
靠按序申請資源來預防。按某一順序申請資源,釋放資源則反序釋放。破壞循環等待條件。
new一個 Thread,線程進入了新建狀態;調用 start()
方法,會啓動一個線程並使線程進入了就緒狀態,當分配到時間片後就能夠開始運行了。 start()
會執行線程的相應準備工做,而後自動執行 run()
方法的內容,這是真正的多線程工做。 而直接執行 run()
方法,會把 run 方法當成一個 main 線程下的普通方法去執行,並不會在某個線程中執行它,因此這並非多線程工做。