1. 程式人生 > >[不得不知道系列]Java執行緒面試你不得不知道的基礎知識一

[不得不知道系列]Java執行緒面試你不得不知道的基礎知識一

1. [Java記憶體管理面試指南一](https://zthinker.com/archives/java-memory-interview-1) 2. [Java基礎面試指南一](https://zthinker.com/archives/java-basic-interview-1) 3. [Java基礎面試指南二](https://zthinker.com/archives/java-basic-interview-2) 4. [Java基礎面試指南三](https://zthinker.com/archives/java-basic-interview-3) 5. [Java基礎面試指南四](https://zthinker.com/archives/java-basic-interview-4) 6. [Java執行緒面試指南一](https://zthinker.com/archives/java-thread-interview-1) 7. [Java執行緒面試指南二](https://zthinker.com/archives/java-thread-interview-2) 8. [Redis面試指南一](https://zthinker.com/archives/redis-interview-1) 9. [Kafka面試指南一](https://zthinker.com/archives/kafka-interview-1) 10. [Spring面試指南一](https://zthinker.com/archives/spring-interview-1) 11. [SpringBoot面試指南一](https://zthinker.com/archives/springboot-interview-1) 12. [微服務面試指南一](https://zthinker.com/archives/microservice-interview-1) Java支援單執行緒以及多執行緒操作。執行緒是程式的執行路徑。今天編寫的大多數程式都是作為單執行緒執行的,當需要同時發生多個事件或動作時會引起問題。單執行緒程式具有一個入口點(main()方法)和一個出口點。 可以使用兩種不同的機制來建立執行緒: 擴充套件Thread類 實現Runnable介面 ##### [Java中的執行緒是什麼?](#) * 執行緒以最佳方式消耗CPU,因此可以進行多處理。多執行緒減少了CPU的空閒時間,從而提高了應用程式的效能。 * 執行緒是輕量級的過程。 * 執行緒類屬於java.lang包。 * 即使我們不建立任何執行緒,我們也可以在Java中建立多個執行緒,至少一個執行緒確實存在,即主執行緒。 * 多個執行緒在Java中並行執行。 * 執行緒有自己的堆疊。 * 執行緒的優點:假設一個執行緒需要10分鐘才能完成某些任務,一次使用10個執行緒可以在1分鐘內完成該任務,因為執行緒可以並行執行。 ##### [Java中的Process和Thread有什麼區別?](#) 一個程序可以有多個執行緒, 執行緒是程序的細分。一個或多個執行緒在程序的上下文中執行。執行緒可以執行過程的任何部分。程序的同一部分可以由多個執行緒執行。 程序具有其父程序的資料段的副本,而執行緒可以直接訪問其程序的資料段。 程序具有自己的地址,而執行緒共享建立它的程序的地址空間。 流程建立需要完成很多工作,我們可能需要複製整個父流程,但是可以輕鬆建立執行緒。 程序可以輕鬆地與子程序進行通訊,但是程序間的通訊很困難。同時,執行緒可以使用wait()和notify()方法輕鬆地與同一程序的其他執行緒通訊。 在處理過程中,所有執行緒共享系統資源(例如堆記憶體等),而執行緒具有其自己的堆疊。 對程序所做的任何更改都不會影響子程序,但是對執行緒所做的任何更改都可能影響該程序其他執行緒的行為。 檢視在不同程序和相同程序上建立執行緒的位置的示例。 ##### [如何在Java中實現執行緒?](#) 這是非常基本的執行緒問題。可以通過兩種方法來建立執行緒:通過實現java.lang.Runnable介面或擴充套件java.lang.Thread類,然後擴充套件run方法。 執行緒有其自己的變數和方法,它在堆上生存和死亡。但是執行執行緒是具有自己的呼叫堆疊的單個程序。執行緒是Java中的輕量級程序。 通過實現java.lang.Runnable interface建立執行緒。 我們將建立實現Runnable介面的類的物件: ``` MyRunnable runnable=new MyRunnable(); Thread thread=new Thread(runnable); ``` 然後通過呼叫建構函式並傳遞Runnable介面的引用(即runnable object)來建立Thread物件: ``` Thread thread=new Thread(runnable); ``` ##### [執行緒是否實現自己的堆疊,如果是,如何實現?](#) 是的,執行緒有自己的堆疊。這是一個非常有趣的問題,面試官傾向於檢查您有關執行緒如何內部維護自己的堆疊的基本知識。 ##### [我們應該實現Runnable介面或擴充套件Thread類。實現Runnable和擴充套件Thread有什麼區別?](#) 很好的答案是,只有在您想要修改run()和其他方法時,才必須擴充套件Thread。如果只想修改實現Runnable的run()方法是最好的選擇(Runnable介面只有一個抽象方法,即run())。  ##### [實現Runnable介面和擴充套件Thread類之間有什麼區別?](#) java中不允許多重繼承:當我們實現Runnable介面時,我們也可以擴充套件另一個類,但是如果我們擴充套件Thread類,則不能擴充套件任何其他類,因為Java不允許多重繼承。因此,通過實現Runnable和擴充套件Thread可以完成相同的工作,但是在實現Runnable的情況下,我們仍然可以選擇擴充套件其他類。因此,最好實現Runnable。 * 執行緒安全性:當我們實現Runnable介面時,同一物件在多個執行緒之間共享,但是當我們擴充套件Thread類時,每個執行緒都與新物件相關聯。 * 繼承(實現Runnable是輕量級操作):當我們不必要地擴充套件Thread時,所有Thread類的功能都會被繼承,但是當我們實現Runnable介面時,不會繼承任何額外的功能,因為Runnable僅包含一個抽象方法,即run()方法。因此,實現Runnable是輕量級的操作。 * 編碼到介面:甚至java也建議編碼到介面。因此,我們必須實現Runnable而不是擴充套件執行緒。另外,Thread類實現Runnable介面。 * 除非要修改類的基本行為,否則不要擴充套件,Runnable介面只有一個抽象方法,即run():僅當您希望修改run()和其他方法時,才必須擴充套件Thread。如果只想修改實現Runnable的run()方法是最好的選擇(Runnable介面只有一個抽象方法,即run())。除非我們要修改Thread類的基本行為,否則我們不能擴充套件Thread類。 * 實現Runnable時程式碼的靈活性:當我們首先擴充套件Thread時,所有執行緒功能都被繼承,並且我們的類成為Thread的直接子類,因此我們正在做的任何操作都在Thread類中。但是,當我們實現Runnable時,我們建立了一個新執行緒並將runnable物件作為引數傳遞,我們可以將runable物件傳遞給執行程式Service等等。因此,當我們實現Runnable時,我們有更多選擇,並且程式碼變得更加靈活。 * Executor Service:如果我們實現Runnable,則可以使用Executor Service啟動在可執行物件上建立的多個執行緒(因為我們可以使用新執行緒來啟動Runnable物件),但是在擴充套件Thread的情況下則不會(因為執行緒只能啟動一次) 。 ##### [怎麼說執行緒的行為是不可預測的?](#) 問題的解決方案非常簡單,執行緒行為是不可預測的,因為執行緒的執行取決於執行緒排程程式,執行緒排程程式在Windows,Unix等不同平臺上的實現方式可能不同。即使在同一平臺上,相同的執行緒程式在後續執行中也可能產生不同的輸出。 為了實現這一點,我們將在同一個Runnable物件上建立2個執行緒,在run()方法中建立for迴圈並啟動兩個執行緒。不能確定哪個執行緒將首先完成,兩個執行緒都將在for迴圈中匿名輸入。 ##### [當執行緒不是Java中的輕量級程序時?](#) 僅當同一程序的執行緒同時執行時,執行緒才是輕量級程序。但是,如果不同程序的執行緒同時執行,那麼執行緒就是重量級程序。 ##### [如何確保從main開始的所有執行緒必須按它們開始的順序結束,並且main應該最後結束?](#) 面試官傾向於瞭解面試者對Thread方法的瞭解。因此,現在該通過正確回答來證明您的觀點了。我們可以使用join()方法來確保所有從main開始的執行緒必須按照它們開始的順序結束,並且main也應該在最後結束。換句話說,等待該執行緒死亡。內部呼叫join()方法將呼叫join(0); 詳細說明:Join()方法–確保從main開始的所有執行緒必須按它們開始的順序結束,並且main也應最後結束。具有程式的join()方法的型別-join的10個顯著特徵。 ##### [使用run()和start()方法啟動執行緒之間的區別是什麼?](#) 這是一個非常有趣的問題,它可能會使您感到困惑,並且有時可能使您認為使用run()和start()方法啟動執行緒之間確實存在任何區別。 當您呼叫start()方法時,主執行緒在內部呼叫run()方法以啟動新建立的執行緒,因此run()方法最終被新建立的執行緒呼叫。 當您呼叫run()方法主執行緒而不是使用新執行緒啟動run()方法,它將自行啟動run()方法。 ##### [使用Volatile關鍵字有什麼意義?](#) Java允許執行緒訪問共享變數。通常,為確保一致地更新共享變數,執行緒應通過獲取對這些共享變數強制執行互斥的鎖來確保其專有使用此類變數。 如果將欄位宣告為易失性,則在這種情況下,Java記憶體模型可確保所有執行緒看到的變數值均