1. 程式人生 > 其它 >面試彙總7.16

面試彙總7.16

1. 如何保證執行緒安全?

通過合理的時間排程,避開共享資源的存取衝突。另外,在並行任務設計上可以通過適當的策略,保證任務與任務之間不存在共享資源,設計一個規則來保證一個客戶的計算工作和資料訪問只會被一個執行緒或一臺工作機完成,而不是把一個客戶的計算工作分配給多個執行緒去完成。

2. 請你簡要說明一下執行緒的基本狀態以及狀態之間的關係?

其中Running表示執行狀態,Runnable表示就緒狀態(萬事俱備,只欠CPU),Blocked表示阻塞狀態,阻塞狀態又有多種情況,可能是因為呼叫wait()方法進入等待池,也可能是執行同步方法或同步程式碼塊進入等鎖池,或者是呼叫了sleep()方法或join()方法等待休眠或其他執行緒結束,或是因為發生了I/O中斷。

3. 請你解釋一下什麼是執行緒池(thread pool)?

執行緒池顧名思義就是事先建立若干個可執行的執行緒放入一個池(容器)中,需要的時候從池中獲取執行緒不用自行建立,使用完畢不需要銷燬執行緒而是放回池中,從而減少建立和銷燬執行緒物件的開銷。

Java 5+中的Executor介面定義一個執行執行緒的工具。它的子型別即執行緒池介面是ExecutorService。要配置一個執行緒池是比較複雜的,尤其是對於執行緒池的原理不是很清楚的情況下,因此在工具類Executors面提供了一些靜態工廠方法,生成一些常用的執行緒池,如下所示:
- newSingleThreadExecutor:建立一個單執行緒的執行緒池。這個執行緒池只有一個執行緒在工作,也就是相當於單執行緒序列執行所有任務。如果這個唯一的執行緒因為異常結束,那麼會有一個新的執行緒來替代它。此執行緒池保證所有任務的執行順序按照任務的提交順序執行。
- newFixedThreadPool:建立固定大小的執行緒池。每次提交一個任務就建立一個執行緒,直到執行緒達到執行緒池的最大大小。執行緒池的大小一旦達到最大值就會保持不變,如果某個執行緒因為執行異常而結束,那麼執行緒池會補充一個新執行緒。
- newCachedThreadPool:建立一個可快取的執行緒池。如果執行緒池的大小超過了處理任務所需要的執行緒,那麼就會回收部分空閒(60秒不執行任務)的執行緒,當任務數增加時,此執行緒池又可以智慧的新增新執行緒來處理任務。此執行緒池不會對執行緒池大小做限制,執行緒池大小完全依賴於作業系統(或者說JVM)能夠建立的最大執行緒大小。
- newScheduledThreadPool:建立一個大小無限的執行緒池。此執行緒池支援定時以及週期性執行任務的需求。
- newSingleThreadExecutor:建立一個單執行緒的執行緒池。此執行緒池支援定時以及週期性執行任務的需求。

4. 舉例說明同步和非同步?

如果系統中存在臨界資源(資源數量少於競爭資源的執行緒數量的資源),例如正在寫的資料以後可能被另一個執行緒讀到,或者正在讀的資料可能已經被另一個執行緒寫過了,那麼這些資料就必須進行同步存取(資料庫操作中的排他鎖就是最好的例子)。當應用程式在物件上呼叫了一個需要花費很長時間來執行的方法,並且不希望讓程式等待方法的返回時,就應該使用非同步程式設計,在很多情況下采用非同步途徑往往更有效率。事實上,所謂的同步就是指阻塞式操作,而非同步就是非阻塞式操作。

5. 請介紹一下執行緒同步和執行緒排程的相關方法?

- wait():使一個執行緒處於等待(阻塞)狀態,並且釋放所持有的物件的鎖;
- sleep():使一個正在執行的執行緒處於睡眠狀態,是一個靜態方法,呼叫此方法要處理InterruptedException異常;
- notify():喚醒一個處於等待狀態的執行緒,當然在呼叫此方法的時候,並不能確切的喚醒某一個等待狀態的執行緒,而是由JVM確定喚醒哪個執行緒,而且與優先順序無關;
- notityAll():喚醒所有處於等待狀態的執行緒,該方法並不是將物件的鎖給所有執行緒,而是讓它們競爭,只有獲得鎖的執行緒才能進入就緒狀態;
通過Lock介面提供了顯式的鎖機制(explicit lock),增強了靈活性以及對執行緒的協調。Lock介面中定義了加鎖(lock())和解鎖(unlock())的方法,同時還提供了newCondition()方法來產生用於執行緒之間通訊的Condition物件。

6. 請簡述一下執行緒的sleep()方法和yield()方法有什麼區別?

①sleep()方法給其他執行緒執行機會時不考慮執行緒的優先順序,因此會給低優先順序的執行緒以執行的機會;yield()方法只會給相同優先順序或更高優先順序的執行緒以執行的機會;

② 執行緒執行sleep()方法後轉入阻塞(blocked)狀態,而執行yield()方法後轉入就緒(ready)狀態;
③ sleep()方法宣告丟擲InterruptedException,而yield()方法沒有宣告任何異常;
④ sleep()方法比yield()方法(跟作業系統CPU排程相關)具有更好的可移植性。

7. 請分別一下多執行緒和同步有幾種實現方法,並且這些實現方法具體內容都是什麼?

多執行緒有兩種實現方法,分別是繼承Thread類與實現Runnable介面同步的實現方面有兩種,分別是synchronized,wait與notify。

8. 請你說出你所知道的執行緒同步的方法?

wait():使一個執行緒處於等待狀態,並且釋放所持有的物件的lock。
sleep():使一個正在執行的執行緒處於睡眠狀態,是一個靜態方法,呼叫此方法要捕捉InterruptedException異常。
notify():喚醒一個處於等待狀態的執行緒,注意的是在呼叫此方法的時候,並不能確切的喚醒某一個等待狀態的執行緒,而是由JVM確定喚醒哪個執行緒,而且不是按優先順序。
Allnotity():喚醒所有處入等待狀態的執行緒,注意並不是給所有喚醒執行緒一個物件的鎖,而是讓它們競爭。

9. 啟動一個執行緒是用run()還是start()?

啟動一個執行緒是呼叫start()方法,使執行緒所代表的虛擬處理機處於可執行狀態,這意味著它可以由JVM排程並執行。這並不意味著執行緒就會立即執行。run()方法可以產生必須退出的標誌來停止一個執行緒。

10. 請你說明一下執行緒中的同步和非同步有何異同?並且舉例說明一下在什麼情況下會使用到同步和非同步?

如果資料將線上程間共享。例如正在寫的資料以後可能被另一個執行緒讀到,或者正在讀的資料可能已經被另一個執行緒寫過了,那麼這些資料就是共享資料,必須進行同步存取。
當應用程式在物件上呼叫了一個需要花費很長時間來執行的方法,並且不希望讓程式等待方法的返回時,就應該使用非同步程式設計,在很多情況下采用非同步途徑往往更有效率。

11. 請說明一下sleep()和wait()有什麼區別?

sleep是執行緒類(Thread)的方法,導致此執行緒暫停執行指定時間,把執行機會給其他執行緒,但是監控狀態依然保持,到時後會自動恢復。呼叫sleep不會釋放物件鎖。
wait是Object類的方法,對此物件呼叫wait方法導致本執行緒放棄物件鎖,進入等待此物件的等待鎖定池,只有針對此物件發出notify方法(或notifyAll)後本執行緒才進入物件鎖定池準備獲得物件鎖進入執行狀態。

12. 請你說明一下在監視器(Monitot)內部,是如何做到執行緒同步的?在程式又應該做那種級別的同步呢?

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

13. 請分析一下同步方法和同步程式碼塊的區別是什麼?

區別:
同步方法預設用this或者當前類class物件作為鎖;
同步程式碼塊可以選擇以什麼來加鎖,比同步方法要更細顆粒度,我們可以選擇只同步會發生同步問題的部分程式碼而不是整個方法。

14. 請詳細描述一下執行緒從建立到死亡的幾種狀態都有哪些?

1. 新建( new ):新建立了一個執行緒物件。
2. 可執行( runnable ):執行緒物件建立後,其他執行緒(比如 main 執行緒)呼叫了該物件 的 start ()方法。該狀態的執行緒位於可執行執行緒池中,等待被執行緒排程選中,獲 取 cpu 的使用權 。
3. 執行( running ):可執行狀態( runnable )的執行緒獲得了 cpu 時間片( timeslice ) ,執行程式程式碼。
4. 阻塞( block ):阻塞狀態是指執行緒因為某種原因放棄了 cpu 使用權,也即讓出了 cpu timeslice ,暫時停止執行。直到執行緒進入可執行( runnable )狀態,才有 機會再次獲得 cpu timeslice 轉到執行( running )狀態。阻塞的情況分三種:
(一). 等待阻塞:執行( running )的執行緒執行 o . wait ()方法, JVM 會把該執行緒放 入等待佇列( waitting queue )中。
(二). 同步阻塞:執行( running )的執行緒在獲取物件的同步鎖時,若該同步鎖 被別的執行緒佔用,則 JVM 會把該執行緒放入鎖池( lock pool )中。
(三). 其他阻塞: 執行( running )的執行緒執行 Thread . sleep ( long ms )或 t . join ()方法,或者發出了 I / O 請求時, JVM 會把該執行緒置為阻塞狀態。 當 sleep ()狀態超時、 join ()等待執行緒終止或者超時、或者 I / O 處理完畢時,執行緒重新轉入可執行( runnable )狀態。
5. 死亡( dead ):執行緒 run ()、 main () 方法執行結束,或者因異常退出了 run ()方法,則該執行緒結束生命週期。死亡的執行緒不可再次復生。