談談這幾個常見的多執行緒面試題

bluepeach發表於2021-09-09

建立執行緒有幾種不同的方式?你喜歡哪一種?為什麼?

有三種方式可以用來建立執行緒:

  • 繼承Thread類

  • 實現Runnable介面

  • 應用程式可以使用Executor框架來建立執行緒池

實現Runnable介面這種方式更受歡迎,因為這不需要繼承Thread類。在應用設計中已經繼承了別的物件的情況下,這需要多繼承(而Java不支援多繼承),只能實現介面。同時,執行緒池也是非常高效的,很容易實現和使用。

概括的解釋下執行緒的幾種可用狀態。

新建( new ):新建立了一個執行緒物件;

可執行( runnable ):執行緒物件建立後,其他執行緒(比如 main 執行緒)呼叫了該物件的 start ()方法。該狀態的執行緒位於可執行執行緒池中,等待被執行緒排程選中,獲 取CPU的使用權;

執行( running ):可執行狀態( runnable )的執行緒獲得了CPU時間片( timeslice ) ,執行程式程式碼;

阻塞( block ):阻塞狀態是指執行緒因為某種原因放棄了CPU 使用權,也即讓出了 CPU timeslice ,暫時停止執行。直到執行緒進入可執行( runnable )狀態,才有 機會再次獲得 cpu timeslice 轉到執行( running )狀態。

阻塞的情況分三種:

  1. 等待阻塞:執行( running )的執行緒執行 o . wait ()方法, JVM 會把該執行緒放 入等待佇列( waitting queue )中。

  2. 同步阻塞:執行( running )的執行緒在獲取物件的同步鎖時,若該同步鎖被別的執行緒佔用,則 JVM 會把該執行緒放入鎖池( lock pool )中。

  3. 其他阻塞: 執行( running )的執行緒執行 Thread . sleep ( long ms )或 t . join ()方法,或者發出了 I / O 請求時, JVM 會把該執行緒置為阻塞狀態。當 sleep ()狀態超時、 join ()等待執行緒終止或者超時、或者 I / O 處理完畢時,執行緒重新轉入可執行( runnable )狀態。

死亡( dead ):執行緒 run ()、 main () 方法執行結束,或者因異常退出了 run ()方法,則該執行緒結束生命週期。死亡的執行緒不可再次復生。

同步方法和同步程式碼塊的區別是什麼?

區別:

  • 同步方法預設用this或者當前類class物件作為鎖;

  • 同步程式碼塊可以選擇以什麼來加鎖,比同步方法要更細顆粒度,我們可以選擇只同步會發生同步問題的部分程式碼而不是整個方法;

在監視器(Monitor)內部,是如何做執行緒同步的?程式應該做哪種級別的同步?

監視器和鎖在Java虛擬機器中是一塊使用的。監視器監視一塊同步程式碼塊,確保一次只有一個執行緒執行同步程式碼塊。每一個監視器都和一個物件引用相關聯。執行緒在獲取鎖之前不允許執行同步程式碼。

java 還提供了顯式監視器( Lock )和隱式監視器( synchronized )兩種鎖方案。

什麼是死鎖(deadlock)?

兩個執行緒或兩個以上執行緒都在等待對方執行完畢才能繼續往下執行的時候就發生了死鎖。結果就是這些執行緒都陷入了無限的等待中。

如何確保N個執行緒可以訪問N個資源同時又不導致死鎖?

多執行緒產生死鎖的四個必要條件:

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

  • 保持和請求條件:一個程式因請求資源而阻塞時,對已獲得資源保持不放。

  • 不可剝奪性:程式已獲得資源,在未使用完成前,不能被剝奪。

  • 迴圈等待條件(閉環):若干程式之間形成一種頭尾相接的迴圈等待資源關係。

只要破壞其中任意一個條件,就可以避免死鎖

一種非常簡單的避免死鎖的方式就是:指定獲取鎖的順序,並強制執行緒按照指定的順序獲取鎖。因此,如果所有的執行緒都是以同樣的順序加鎖和釋放鎖,就不會出現死鎖了。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/2558/viewspace-2797885/,如需轉載,請註明出處,否則將追究法律責任。

相關文章