1. 程式人生 > >25道多執行緒面試題,附帶答案(一)

25道多執行緒面試題,附帶答案(一)

1.什麼是程序?

是一個具有一定獨立功能的程式在一個數據集上的一次動態執行的過程,是作業系統進行資源分配和排程的一個獨立單位,是應用程式執行的載體。

 

2.什麼是執行緒?

執行緒是作業系統能夠進行運算排程的最小單位。

它被包含在程序之中,是程序中的實際運作單位。

一條執行緒指的是程序中一個單一順序的控制流,一個程序中可以併發多個執行緒,每條執行緒並行執行不同的任務。

 

3.程序和執行緒的區別?

程序和執行緒的主要差別在於它們是不同的作業系統資源管理方式。

程序有獨立的地址空間,一個程序崩潰後,在保護模式下不會對其它程序產生影響。

執行緒只是一個程序中的不同執行路徑。執行緒有自己的堆疊和區域性變數,但執行緒之間沒有單獨的地址空間,一個執行緒死掉就等於整個程序死掉,所以多程序的程式要比多執行緒的程式健壯,但在程序切換時,耗費資源較大,效率要差一些。

但對於一些要求同時進行並且又要共享某些變數的併發操作,只能用執行緒,不能用程序。

 

4.多執行緒和單執行緒有什麼區別?

單執行緒程式:程式執行過程中只有一個有效操作的序列,不同操作之間都有明確的執行先後順序,容易出現程式碼阻塞

多執行緒程式:有多個執行緒,執行緒間獨立執行,能有效地避免程式碼阻塞,並且提高程式的執行效能

 

5.為什麼要使用多執行緒?

(1)使用多執行緒可以減少程式的響應時間。 在單執行緒的情況下,如果某個程式很耗時或者陷入長時間等待(如等待網路響應),此時程式將不會相應滑鼠和鍵盤等操作,使用多執行緒後,可以把這個耗時的執行緒分配到一個單獨的執行緒去執行,從而是程式具備了更好的互動性。

(2)與程序相比,執行緒的建立和切換開銷更小。 由於啟動一個新的執行緒必須給這個執行緒分配獨立的地址空間,建立許多資料結構來維護執行緒程式碼段、資料段等資訊,而運行於同一個程序內的執行緒共享程式碼段、資料段,執行緒的啟動或切換的開銷就比程序要少很多。同時多執行緒在資料共享方面效率非常高。

(3)多CPU或多核心計算機本身就具有執行多執行緒的能力。 如果使用單個執行緒,將無法重複利用計算機資源,造成資源的巨大浪費。因此在多CPU計算機上使用多執行緒能提高CPU的利用率。

(4)使用多執行緒能簡化程式的結構,使用程式便於理解和維護。 一個非常複雜的程序可以分成多個執行緒來執行。

 

6.什麼是執行緒安全?

當多個執行緒訪問同一個物件時,如果不用考慮這些執行緒在執行時環境下的排程和交替執行,也不需要進行額外的同步,或者在呼叫方進行任何其他的協調操作,呼叫這個物件的行為都可以獲取正確的結果,那這個物件是執行緒安全的。 ——<<深入Java虛擬機器>>

 

7.為何要使用執行緒同步?

Java允許多執行緒併發控制,當多個執行緒同時操作一個可共享的資源變數時(如資料的增刪改查),將會導致資料不準確,相互之間產生衝突。

因此加入同步鎖以避免在該執行緒沒有完成操作之前,被其他執行緒的呼叫,從而保證了該變數的唯一性和準確性。

 

8.如何確保執行緒安全?

  • 對非安全的程式碼進行加鎖控制
  • 使用執行緒安全的類
  • 多執行緒併發情況下,執行緒共享的變數改為方法級的區域性變數

 

9.什麼是原子操作?

檢查數值、改變數值,以及可能發生的睡眠操作均作為單一的、不可分割的原子操作完成。

 

10Java記憶體模型是什麼?

  1. Java中所有變數都儲存在主存中,對於所有執行緒都是共享的(因為在同一程序中),每個執行緒都有自己的工作記憶體或本地記憶體(Working Memory),工作記憶體中儲存的是主存中某些變數的拷貝,執行緒對所有變數的操作都是在工作記憶體中進行,而執行緒之間無法相互直接訪問,變數傳遞均需要通過主存完成,但是在程式內部可以互相呼叫(通過物件方法),所有執行緒間的通訊相對簡單,速度也很快。 

《25道多執行緒面試題(一)》

2、程序間的內部資料和狀態都是相互完全獨立的,因此程序間通訊大多數情況是必須通過網路實現。執行緒本身的資料,通常只有暫存器資料,以及一個程式執行時使用的堆疊,所以執行緒的切換比程序切換的負擔要小。

3、CPU對於各個執行緒的排程是隨機的(分時排程),在Java程式中,JVM負責執行緒的排程。 執行緒排程是指按照特定的機制為多個執行緒分配CPU的使用權,也就是實際執行的時候是執行緒,因此CPU排程的最小單位是執行緒,而資源分配的最小單位是程序。

 

11.Java中堆和棧有什麼不同?

棧:在函式中定義的基本型別的變數和物件的引用變數都是在函式的棧記憶體中分配。

堆:堆記憶體用於存放由new建立的物件和陣列。

從通俗化的角度來說,堆是用來存放物件的,棧是用來存放執行程式的

 

12.JVM中哪個引數是用來控制執行緒的棧堆疊小的

-Xss引數用來控制執行緒的堆疊大小。

 

13.什麼是競態條件?你怎樣發現和解決競爭?

當兩個執行緒競爭同一資源時,如果對資源的訪問順序敏感,就稱存在競態條件。

在臨界區中使用適當的同步就可以避免競態條件。

界區實現方法有兩種,一種是用synchronized,一種是用Lock顯式鎖實現。

 

14.執行緒安全的級別

不可變

不可變的物件一定是執行緒安全的,並且永遠也不需要額外的同步。

Java類庫中大多數基本數值類如Integer、String和BigInteger都是不可變的。

 

15.無條件的執行緒安全

由類的規格說明所規定的約束在物件被多個執行緒訪問時仍然有效,不管執行時環境如何排列,執行緒都不需要任何額外的同步。

如 Random 、ConcurrentHashMap、Concurrent集合、atomic

 

16有條件的執行緒安全

有條件的執行緒安全類對於單獨的操作可以是執行緒安全的,但是某些操作序列可能需要外部同步。

有條件執行緒安全的最常見的例子是遍歷由 Hashtable 或者 Vector 或者返回的迭代器

 

17.非執行緒安全(執行緒相容)

執行緒相容類不是執行緒安全的,但是可以通過正確使用同步而在併發環境中安全地使用。

如ArrayList HashMap

 

18.執行緒對立

執行緒對立是那些不管是否採用了同步措施,都不能在多執行緒環境中併發使用的程式碼。

如如System.setOut()、System.runFinalizersOnExit()

 

19.你對執行緒優先順序的理解是什麼?

每一個執行緒都是有優先順序的,一般來說,高優先順序的執行緒在執行時會具有優先權,但這依賴於執行緒排程的實現,這個實現是和作業系統相關的(OSdependent)。

可以定義執行緒的優先順序,但是這並不能保證高優先順序的執行緒會在低優先順序的執行緒前執行。執行緒優先順序是一個int變數(從1-10),1代表最低優先順序,10代表最高優先順序。

 

20.什麼是執行緒排程器(Thread Scheduler)和時間分片(Time Slicing)?

執行緒排程器是一個作業系統服務,它負責為Runnable狀態的執行緒分配CPU時間。一旦建立一個執行緒並啟動它,它的執行便依賴於執行緒排程器的實現。

時間分片是指將可用的CPU時間分配給可用的Runnable執行緒的過程。分配CPU時間可以基於執行緒優先順序或者執行緒等待的時間。

執行緒排程並不受到Java虛擬機器控制,所以由應用程式來控制它是更好的選擇。

 

21.在多執行緒中,什麼是上下文切換(context-switching)?

單核CPU也支援多執行緒執行程式碼,CPU通過給每個執行緒分配CPU時間片來實現這個機制。時間片是CPU分配給各個執行緒的時間,因為時間片非常短,所以CPU通過不停地切換執行緒執行,讓我們感覺多個執行緒時同時執行的,時間片一般是幾十毫秒(ms)。

作業系統中,CPU時間分片切換到另一個就緒的執行緒,則需要儲存當前執行緒的執行的位置,同時需要載入需要恢復執行緒的環境資訊。

 

22.使用者執行緒和守護執行緒有什麼區別?

守護執行緒都是為JVM中所有非守護執行緒的執行提供便利服務: 只要當前JVM例項中尚存在任何一個非守護執行緒沒有結束,守護執行緒就全部工作;只有當最後一個非守護執行緒結束時,守護執行緒隨著JVM一同結束工作。

User和Daemon兩者幾乎沒有區別,唯一的不同之處就在於虛擬機器的離開:如果 User Thread已經全部退出執行了,只剩下Daemon Thread存在了,虛擬機器也就退出了。

因為沒有了被守護者,Daemon也就沒有工作可做了,也就沒有繼續執行程式的必要了。

 

23.如何建立守護執行緒?以及在什麼場合來使用它?

任何執行緒都可以設定為守護執行緒和使用者執行緒,通過方法Thread.setDaemon(bool on);true則把該執行緒設定為守護執行緒,反之則為使用者執行緒。Thread.setDaemon()必須在Thread.start()之前呼叫,否則執行時會丟擲異常。

守護執行緒相當於後臺管理者 比如 : 進行記憶體回收,垃圾清理等工作

 

24.執行緒的狀態轉換?

《25道多執行緒面試題(一)》

1、新建狀態(New):新建立了一個執行緒物件。

2、就緒狀態(Runnable):執行緒物件建立後,其他執行緒呼叫了該物件的start()方法。該狀態的執行緒位於可執行執行緒池中,變得可執行,等待獲取CPU的使用權。

3、執行狀態(Running):就緒狀態的執行緒獲取了CPU,執行程式程式碼。

4、阻塞狀態(Blocked):阻塞狀態是執行緒因為某種原因放棄CPU使用權,暫時停止執行。直到執行緒進入就緒狀態,才有機會轉到執行狀態。阻塞的情況分三種: (一)、等待阻塞:執行的執行緒執行wait()方法,JVM會把該執行緒放入等待池中。(wait會釋放持有的鎖) (二)、同步阻塞:執行的執行緒在獲取物件的同步鎖時,若該同步鎖被別的執行緒佔用,則JVM會把該執行緒放入鎖池中。 (三)、其他阻塞:執行的執行緒執行sleep()或join()方法,或者發出了I/O請求時,JVM會把該執行緒置為阻塞狀態。當sleep()狀態超時、join()等待執行緒終止或者超時、或者I/O處理完畢時,執行緒重新轉入就緒狀態。(注意,sleep是不會釋放持有的鎖)

5、死亡狀態(Dead):執行緒執行完了或者因異常退出了run()方法,該執行緒結束生命週期。

 

25.執行緒中斷是否能直接呼叫stop,為什麼?

Java提供的終止方法只有一個stop,但是不建議使用此方法,因為它有以下三個問題:

  1. stop方法是過時的 從Java編碼規則來說,已經過時的方式不建議採用.
  2. stop方法會導致程式碼邏輯不完整 stop方法是一種”惡意”的中斷,一旦執行stop方法,即終止當前正在執行的執行緒,不管執行緒邏輯是否完整,這是非常危險的.

 

原文:Java架構筆記

免費Java高階資料需要自己領取,涵蓋了Java、Redis、MongoDB、MySQL、Zookeeper、Spring Cloud、Dubbo高併發分散式等教程,一共30G。   
傳送門:    https://mp.weixin.qq.com/s/JzddfH-7yNudmkj