Java线程面试的前50个问题,面向初学者和经验丰富的程序员
您可以參加任何Java面試,無論是大四還是中級,經驗或新來的人,一定會看到線??程,并發和多線程中的幾個問題。 實際上,這種內置的并發支持是Java編程語言的最強優勢之一,并幫助它在企業界和程序員中同樣流行。 大多數有利可圖的Java開發人員職位都需要出色的Java多核核心技能,以及在開發,調試和調整高性能低延遲并發Java應用程序方面的經驗。
這就是原因,它是面試中最受歡迎的技能之一。 在典型的Java采訪中,Interviewer通過詢問以下問題逐漸從Thread的基本概念開始,例如您為什么需要線程,如何創建線程,這是一種更好的創建線程的方法,例如通過擴展線程類或實現Runnable ,然后慢慢進入并發問題,并發Java應用程序開發過程中面臨的挑戰,Java內存模型,JDK 1.5中引入的高級并發實用程序,并發Java應用程序的原理和設計模式,經典的多線程問題,例如生產者消費者,就餐哲學家,讀者作家或簡單地有界緩沖區問題。 由于僅了解線程基礎知識還不夠,因此您必須知道如何處理并發問題,例如死鎖 , 競爭條件 ,內存不一致以及各種與線程安全有關的問題。 通過提出各種多線程和并發問題,對這些技能進行了全面測試。 許多Java開發人員習慣于在進行面試之前僅閱讀和閱讀面試問題,這還不錯,但是您應該相距不遠。 同樣,收集問題并進行相同的練習也很費時間,這就是為什么我創建了這份清單,該清單是從各種采訪中收集的與Java多線程和并發相關的前50個問題 。 當我發現它們時,我只會添加新的和最近的面試問題。 順便說一句,我還沒有在這里提供這個問題的答案,為什么? 因為我希望大多數Java開發人員都知道該問題的答案,如果不是,那么使用Google也可以廣泛獲得答案。 如果找不到任何特定問題的答案,可以隨時在評論部分詢問我。 您甚至可以在提供的鏈接上找到一些問題的答案,也可以在我之前的文章“帶答案的12個Java線程問題”中找到答案 。
這是我們有關Java線程,并發和多線程的主要問題列表。 您可以使用此列表為Java面試做好準備。
線程是獨立的執行路徑。 這是一種利用計算機中可用的多個CPU的方式。 通過使用多個線程,可以加快CPU綁定任務的速度。 例如,如果一個線程完成一項工作需要100毫秒,則可以使用10個線程將該任務減少到10毫秒。 Java在語言級別為多線程提供了出色的支持,并且它也是賣點之一。 有關更多詳細信息, 請參見此處 。
線程是進程的子集,換句話說,一個進程可以包含多個線程。 兩個進程在不同的內存空間上運行,但是所有線程共享相同的內存空間。 不要將此與堆棧內存混淆,堆棧內存對于不同的線程是不同的,并且用于將本地數據存儲到該線程。 有關更多詳細信息,請參見此答案 。
在語言級別,有兩種方法可以用Java實現Thread。 java.lang.Thread的一個實例表示一個線程,但是它需要執行一個任務,它是接口java.lang.Runnable的實例。 由于Thread類本身實現Runnable,因此可以通過擴展Thread類或僅實現Runnable接口來覆蓋run()方法。 有關詳細的答案和討論,請參見本文 。
這是先前的多線程面試問題的跟進。 眾所周知,我們可以通過擴展Thread類或實現Runnable接口來實現線程,出現問題,哪種更好,什么時候使用? 如果您知道Java編程語言不支持類的多重繼承,但是可以使用它實現多個接口,那么這個問題將很容易回答。 這意味著,如果您還想擴展另一個類(例如Canvas或CommandListener),則實現Runnable比擴展Thread更好。 有關更多觀點和討論,您也可以參考這篇文章 。
早期的一個棘手的Java問題,但仍然足以區分對Java線程模型的淺淺理解start()方法用于啟動新創建的線程,而start()內部調用run()方法,調用run有區別()方法直接。 當您以常規方法調用run()時,該方法在同一線程中調用,因此不會啟動新線程,這是您調用start()方法時的情況。 閱讀此答案進行更詳細的討論。
Runnable和Callable都表示打算在單獨的線程中執行的任務。 JDK 1.0提供了Runnable,而JDK 1.5則添加了Callable。 兩者之間的主要區別在于Callable的call()方法可以返回值并引發Exception,而Runnable的run()方法則無法實現。 可調用的返回Future對象,可以保存計算結果。 有關此問題的更深入解答,請參閱我關于相同主題的博客文章 。
盡管CyclicBarrier和CountDownLatch都在一個或多個事件上等待線程數,但是它們之間的主要區別是,一旦計數達到零,您就無法重用CountDownLatch,但是即使在屏障被破壞后,您也可以重用相同的CyclicBarrier。 有關其他幾點和示例代碼示例,請參見此答案 。
Java內存模型是一組規則和準則,可讓Java程序在多個內存體系結構,CPU和操作系統之間確定性地運行。 在多線程的情況下尤其重要。 Java內存模型提供了某種保證,即一個線程所做的更改應對其他線程可見,其中之一是發生在關系之前。 這種關系定義了一些規則,這些規則使程序員可以預期并推理并發Java程序的行為。 例如,之前發生的關系保證:
- 線程中的每個動作都發生-在該線程中的每個動作之后,它們在程序順序中排在后面,這稱為程序順序規則。
- 監視器鎖發生解鎖-在同一監視器鎖上的每個后續鎖之前,也稱為監視器鎖規則。
- 在隨后每次對該字段的讀取之前,都會對volatile字段進行寫操作,這稱為“可變變量規則”。
- 線程上對Thread.start的調用發生-在任何其他線程檢測到線程已終止之前,通過成功從Thread.join()返回或通過Thread.isAlive()返回false(也稱為線程啟動規則)來進行。
- 一個線程在另一個線程上調用中斷發生-被中斷的線程在檢測到中斷之前(通過引發InterruptedException或調用isInterrupted或被中斷),通常稱為線程中斷規則。
- 對象構造函數的結束發生在該對象的終結器開始之前,即終結器規則。
- 如果A發生在B之前,而B發生在C之前,那么A發生在C之前,這意味著在發生之前,保證了傳遞性。
我強烈建議閱讀《 Java并發實踐》第16章,以更詳細地了解Java內存模型。
volatile是一個特殊的修飾符,只能與實例變量一起使用。 在并發Java程序中,在沒有任何同步器(例如,同步關鍵字或鎖)的情況下,其他人看不到多個線程對實例變量進行的更改。 如前一個問題中的“易變變量規則”所述, 易變變量保證寫操作將在后續讀取之前發生。 閱讀此答案以了解有關volatile變量以及何時使用它們的更多信息。
(是的,請參閱詳細信息 )
線程安全性是對象或代碼的屬性,它保證如果多線程以任何方式(例如,讀寫)執行或使用該線程或對象,其行為將與預期的一樣。 例如,如果在多個線程之間共享該計數器的同一實例,則線程安全計數器對象將不會丟失任何計數。 顯然,您也可以將集合類分為兩類,線程安全的和非線程安全的。 Vector確實是線程安全的類,它通過同步修改Vector狀態的方法來實現線程安全,另一方面,其對應的ArrayList不是線程安全的。
當Java程序處于并發執行環境中時,爭用條件是一些細微的編程錯誤的原因。 顧名思義,競爭條件是由于多個線程之間的競爭而產生的,如果本應首先執行的線程丟失了競爭,然后執行了第二,則代碼的行為會發生變化,這會成為不確定的錯誤。 由于線程之間競爭的隨機性,這是最難發現和再現的錯誤之一。 競爭條件的一個示例是亂序處理,有關Java程序中競爭條件的更多示例,請參見此答案 。
我總是說Java為所有事物提供了豐富的API,但具有諷刺意味的是Java沒有提供確定的停止線程的可靠方法。 JDK 1.0中有一些控制方法,例如stop(),suspend()和resume(),由于潛在的死鎖威脅而在以后的版本中不推薦使用,從那時起,Java API設計人員就一直沒有努力提供一致的,線程安全的和優雅的方式來停止線程。 程序員主要依靠這樣的事實,即線程一旦完成run()或call()方法的執行就自動停止。 要手動停止,程序員可以利用易失的布爾變量,并在每次迭代中檢查run方法是否具有循環或中斷線程以突然取消任務。 有關在Java中停止線程的示例代碼,請參見本教程 。
這是我在訪談中看到的一個棘手的Java問題 。 簡而言之,如果未捕獲的線程將死亡,如果注冊了未捕獲的異常處理程序,則它將獲得回調。 Thread.UncaughtExceptionHandler是一個接口,定義為當線程由于未捕獲的異常突然終止時調用的處理程序的嵌套接口。 當線程由于未捕獲的異常而即將終止時,Java虛擬機將使用Thread.getUncaughtExceptionHandler()在線程中查詢其UncaughtExceptionHandler并將調用處理程序的uncaughtException()方法,并將線程和異常作為參數傳遞。
您可以使用共享對象或并發數據結構(例如BlockingQueue)在線程之間共享數據。 請參閱本教程以學習Java中的線程間通信 。 它使用等待和通知方法來實現Producer使用者模式,該模式涉及在兩個線程之間共享對象。
這是Java核心訪談中另一個棘手的問題,因為多個線程可以等待單個監視器鎖定,所以Java API設計器提供了一種方法,用于在等待條件改變后僅通知其中一個或所有通知,但是它們提供了一半的實現。 那里的notify()方法沒有提供任何選擇特定線程的方法,這就是為什么它僅在您知道只有一個線程正在等待時才有用。 另一方面,notifyAll()向所有線程發送通知,并允許它們競爭鎖,這確保了至少一個線程將繼續進行。 有關更詳細的答案和代碼示例,請參見我在類似主題上的博客文章 。
這是一個與設計有關的問題,它檢查候選人對現有系統的看法,或者他是否曾經想到過如此普遍但最初看起來不合適的事物。 為了回答這個問題,您必須給出一些原因,為什么這三個方法在Object類中有意義,而為什么不在Thread類中有意義。 顯而易見的原因之一是Java在對象級別而不是線程級別提供了鎖定。 每個對象都有鎖,該鎖由線程獲取。 現在,如果線程需要等待某些鎖定,則有必要在該對象而不是該線程上調用wait()。 如果在Thread類上聲明了wait()方法,則不清楚哪個線程正在等待。 簡而言之,由于wait,notify和notifyAll在鎖級別進行操作,因此在對象類上定義它是有意義的,因為鎖屬于對象。 您也可以查看本文,以獲取有關此問題的更詳細的答案。
ThreadLocal變量是Java程序員可以使用的特殊類型的變量。 就像實例變量是每個實例一樣,ThreadLocal變量是每個線程。 這是一種實現昂貴創建對象的線程安全的好方法,例如,您可以使用ThreadLocal使SimpleDateFormat成為線程安全的。 由于該類很昂貴,因此在本地范圍內使用它不是很好,因為每次調用都需要單獨的實例。 通過為每個線程提供自己的副本,您可以用一個箭頭射殺兩只鳥。 首先,通過重用固定數量的實例來減少昂貴對象的實例數,其次,無需支付同步或不變性即可實現線程安全。 線程局部變量的另一個很好的例子是ThreadLocalRandom類,它減少了在多線程環境中創建昂貴的Random對象的實例數量。 請參閱此答案以了解有關Java中線程局部變量的更多信息。
FutureTask表示并發Java應用程序中的可取消異步計算。 此類提供Future的基本實現,其中包含啟動和取消計算,查詢以查看計算是否完成以及檢索計算結果的方法。 只有在計算完成后才能檢索結果; 如果計算尚未完成,則get方法將阻塞。 FutureTask對象可用于包裝Callable或Runnable對象。 從FutureTask開始
還實現了Runnable,可以將其提交給執行器以執行。
interrupted()和isInterrupted()之間的主要區別在于,前者清除中斷狀態,而后者不清除。 Java多線程中的中斷機制是使用稱為中斷狀態的內部標志實現的。 通過調用Thread.interrupt()來中斷線程會設置此標志。 當被中斷的線程通過調用靜態方法 Thread.interrupted()檢查中斷時,將清除中斷狀態。 一個線程用于查詢另一線程的中斷狀態的非靜態isInterrupted()方法不會更改中斷狀態標志。 按照慣例,任何通過拋出InterruptedException退出的方法都會清除中斷狀態。 但是,總是有可能通過另一個調用中斷的線程立即再次設置中斷狀態。
從同步塊或方法中調用wait和notify方法的主要原因是Java API強制了它。 如果不從同步上下文中調用它們,則代碼將引發IllegalMonitorStateException。 一個更微妙的原因是避免在等待和通知呼叫之間出現競爭狀態。 要了解更多信息,請在此處查看我的同名帖子。
等待中的線程可能會收到錯誤警報和虛假的喚醒調用,如果它不檢查循環中的等待條件,即使不滿足條件,它也只會退出。 這樣,當等待線程喚醒時,它不能假定其正在等待的狀態仍然有效。 它可能在過去是有效的,但是在調用notify()方法之后以及在等待線程喚醒之前,狀態可能已經更改。 這就是從循環調用wait()方法總是更好的原因,您甚至可以創建用于調用wait的模板并在Eclipse中進行通知。 要了解有關此問題的更多信息,建議您閱讀有關線程和同步的有效Java項目。
盡管同步和并發收集都提供了適用于多線程和并發訪問的線程安全收集,但是后期比以前更具可伸縮性。 在Java 1.5之前,Java程序員只有同步收集,如果多個線程同時訪問它們,它們將成為爭用的源,這會妨礙系統的可伸縮性。 Java 5引入了諸如ConcurrentHashMap之類的并發集合,該集合不僅提供線程安全性,而且還通過使用鎖剝離和對內部表進行分區等現代技術來提高可伸縮性。 有關Java中同步和并發收集之間的更多區別,請參見此答案 。
為什么有人將這個問題作為多線程和并發的一部分? 因為堆棧是與線程緊密相關的內存區域。 為了回答這個問題,堆棧和堆都是Java應用程序中的特定內存。 每個線程都有自己的堆棧,該堆棧用于存儲局部變量,方法參數和調用堆棧。 存儲在一個線程堆棧中的變量對其他線程不可見。 另一方面,堆是所有線程共享的公共內存區域。 在堆內部創建本地或任何級別的對象。 為了提高性能,線程傾向于將值從堆中緩存到堆棧中,如果該變量被多個線程修改,則可能會產生問題,這就是易變變量的所在。 volatile建議線程始終從主內存中讀取變量的值。 請參閱本文以了解有關Java中堆棧和堆的更多信息,以更詳細地回答此問題。
就時間和資源而言,創建線程是昂貴的。 如果在請求處理時創建線程,這會減慢響應時間,并且一個進程只能創建數量有限的線程。 為避免這兩個問題,在應用程序啟動時將創建線程池,并將線程重用于請求處理。 該線程池稱為“線程池”,而線程稱為工作線程。 從JDK 1.5版本開始,Java API提供了Executor框架,該框架允許您創建不同類型的線程池,例如單個線程池(一次處理一個任務),固定線程池(固定數量的線程池)或緩存的線程池。 (一個可擴展的線程池,適用于具有許多短期任務的應用程序)。 請參閱本文以了解有關Java中線程池的更多信息,以準備該問題的詳細答案。
您在現實世界中解決的大多數線程問題都屬于Producer使用者模式類別,其中一個線程正在執行任務,而另一個線程正在使用該線程。 您必須知道如何進行線程間通信以解決此問題。 在最低級別上,您可以使用wait和notify來解決此問題,在最高級別上,您可以利用Semaphore或BlockingQueue來實現Producer使用者模式,如本教程所示。
死鎖是指兩個線程互相等待以采取行動,從而允許它們進一步移動的情況。 這是一個嚴重的問題,因為發生這種情況時,您的程序會掛起,并且沒有執行預期的任務。 為了使死鎖發生,必須滿足以下四個條件:
- 互斥:至少一種資源必須以不可共享的方式持有。 在任何給定時刻,只有一個進程可以使用資源。
- 保留并等待:一個進程當前正在保存至少一個資源,并請求其他進程正在保留的其他資源。
- 否搶占:一旦分配了資源,操作系統不得取消分配資源。 它們必須由持有程序自愿釋放。
- 循環等待:一個進程必須等待另一個進程正在占用的資源,而另一個進程又在等待第一個進程釋放該資源。
避免死鎖的最簡單方法是防止循環退出 ,這可以通過以特定順序獲取鎖并以相反的順序釋放鎖來完成,以便線程僅在持有另一個時才能繼續獲取鎖。 在本教程中查看實際的代碼示例,以及有關避免Java中死鎖的技術的詳細討論。
這個問題是前一次面試問題的延伸。 活鎖類似于死鎖,不同之處在于,活鎖中涉及的線程或進程的狀態彼此之間不斷變化,而沒有任何一個進一步發展。 Livelock是資源匱乏的特例。 當兩個人在狹窄的走廊里相遇時,發生了現實生活中的活鎖例子,每個人都試圖通過移動到一邊讓對方通過而禮貌,但最終卻沒有任何進展就左右搖擺,因為他們都反復移動在同一時間相同的方式。 簡而言之,活動鎖和死鎖之間的主要區別在于,在先前的過程更改狀態下,沒有任何進展。
我什至不知道您可以在Java訪談的電話回合中問到這個問題之前,我是否可以檢查線程是否已經鎖定。 在java.lang.Thread上有一個名為holdLock()的方法,當且僅當當前線程持有對指定對象的監視器鎖時,它才返回true。 您也可以查看本文以獲得更詳細的答案 。
根據操作系統的不同,可以采取多種方式進行Java進程的線程轉儲。 進行線程轉儲時,JVM會在日志文件或標準錯誤控制臺中轉儲所有線程的狀態。 在Windows中,您可以使用Ctrl + Break鍵組合進行線程轉儲,而在Linux上,您可以使用kill -3命令。 您還可以使用名為jstack的工具進行線程轉儲,它對進程ID進行操作,可以使用另一個名為jps的工具找到該進程。
這是簡單的一個,-Xss參數用于控制Java中Thread的堆棧大小。 您可以查看此JVM選項列表,以了解有關此參數的更多信息。
曾經有幾天,在Java中提供互斥的唯一方法是通過synced關鍵字,但是它有幾個缺點,例如您不能將鎖擴展到方法或塊邊界之外,不能放棄嘗試鎖等。Java5解決了這個問題通過Lock接口提供更復雜的控制來解決問題。 ReentrantLock是Lock接口的常見實現,它提供與可同步方法和語句訪問的隱式監視器鎖相同的基本行為和語義的可重入互斥鎖,但具有擴展功能。 請參閱本文,以了解這些功能以及Java中Synchronized與ReentrantLock之間的更多區別。
多線程中的排序可以通過不同的方法來實現,但是您可以簡單地使用線程類的join()方法在另一個線程完成執行時啟動一個線程。 為了確保執行三個線程,您需要首先啟動最后一個線程,例如T3,然后以相反的順序調用join方法,例如T3調用T2。 join,然后T2調用T1.join,這樣T1將首先完成,而T3將最后完成。 要了解有關join方法的更多信息,請參閱本教程 。
Yield方法是請求當前線程放棄CPU以便其他線程可以執行的一種方法。 Yield是一種靜態方法,僅保證當前線程將放棄CPU,但未說明任何其他線程將獲得CPU。 同一線程可能會收回CPU并重新開始執行。 請參閱本文,以了解有關屈服方法的更多信息并更好地回答此問題。
ConcurrentHashMap通過將實際映射劃分為多個部分來實現其可伸縮性和線程安全性。 使用并發級別可以實現此分區。 它是ConcurrentHashMap構造函數的可選參數,默認值為16。表在內部進行了分區,以嘗試允許指定數量的并發更新而不會發生爭用。 要了解有關并發級別和內部調整大小的更多信息,請參閱我的文章ConcurrentHashMap如何在Java中工作 。
Java中的信號量是一種新型的同步器。 這是一個計數信號量。 從概念上講,信號量維護一組許可證。 如有必要,每個Acquisition()都會阻塞,直到獲得許可為止,然后再獲取許可。 每個release()添加一個許可,有可能釋放阻塞獲取者。 但是,沒有使用實際的許可對象。 信號量只是保持可用數量的計數并采取相應措施。 信號量用于保護固定數量的可用昂貴資源,例如池中的數據庫連接。 請參閱本文,以了解有關在Java中對信號量進行計數的更多信息。
這是我列表中另一個棘手的問題。 許多程序員會認為它會阻塞直到任務被清除,但是它是正確的。 如果無法安排執行任務,則ThreadPoolExecutor的Submit()方法將拋出RejectedExecutionException。
兩種方法都是將任務提交到線程池的方法,但是兩者之間略有不同。 execute(Runnable command)在Executor接口中定義,將來會執行給定的任務,但更重要的是它不返回任何內容。 它的返回類型為void。 另一方面,submit()是重載的方法,它可以執行Runnable或Callable任務,并且可以返回可以保存未決計算結果的Future對象。 此方法在ExecutorService接口上定義,該接口擴展了Executor接口,并且其他所有線程池類(例如ThreadPoolExecutor或ScheduledThreadPoolExecutor)都將獲得這些方法。 要了解有關線程池的更多信息,可以查看本文 。
阻塞方法是一種阻塞直到任務完成的方法,例如ServerSocket的accept()方法阻塞直到連接了客戶端。 在這里阻塞意味著直到任務完成之前控制權不會返回給調用者。 另一方面,存在異步或非阻塞方法,該方法甚至在任務完成之前就返回。 要了解有關阻塞方法的更多信息,請參見此答案 。
您可以簡單地將這個問題表示為“不,Swing不是線程安全的”,但是即使面試官沒有詢問,您也必須解釋您的意思。 當我們說swing不是線程安全的時,我們通常引用其組件,該組件不能在多個線程中進行修改。 對GUI組件的所有更新都必須在AWT線程上完成,Swing提供了同步和異步回調方法來安排此類更新。 您也可以閱讀我的文章,以了解有關擺動和線程安全的更多信息,以更好地回答此問題。 甚至接下來的兩個問題也與此概念相關。
這是Swing API為Java開發人員提供的兩種方法,可從事件分發程序線程以外的線程更新GUI組件。 InvokeAndWait()同步更新GUI組件,例如進度條,一旦取得進度,也應更新該條以反映該更改。 如果在另一個線程中跟蹤進度,則必須調用invokeAndWait()來調度事件分派器線程對該組件的更新。 另一方面,invokeLater()是異步調用以更新組件。 您也可以參考此答案以獲取更多信息。
這個問題再次與擺動和線程安全有關,盡管組件不是線程安全的,但是某些方法可以從多個線程安全地調用。 我知道repaint()和revalidate()是線程安全的,但是在不同的swing組件上還有其他方法,例如JTextComponent的setText()方法,JTextArea類的insert()和append()方法。
這個問題看似與多線程和并發無關,但確實如此。 不變性有助于簡化Java中已經很復雜的并發代碼。 由于不共享對象可以共享而無需任何同步,因此Java開發人員非常重視。 旨在在線程之間共享的核心值對象應該是不變的,以提高性能和簡化性。 不幸的是,Java中沒有@Immutable批注,它可以使您的對象不可變,Java開發人員必須完成艱苦的工作。 您需要保留一些基本知識,例如在構造函數中初始化狀態,沒有setter方法,沒有引用泄漏,保留可變對象的單獨副本以創建Immutable對象。 有關逐步指南的信息,請參閱我的文章, 如何使對象在Java中不可變 。 這將為您提供足夠的材料來自信地回答這個問題。
通常,讀寫鎖是鎖剝離技術的結果,可以提高并發應用程序的性能。 在Java中,ReadWriteLock是在Java 5版本中添加的接口。 ReadWriteLock維護一對關聯的鎖,一個用于只讀操作,一個用于寫入。 只要沒有寫程序,讀鎖就可以同時由多個讀程序線程保持。 寫鎖是排他的。 如果愿意,可以使用自己的一組規則來實現此接口,否則可以使用JDK隨附的ReentrantReadWriteLock,它最多支持65535遞歸寫鎖和65535讀鎖。
繁忙旋轉是并發程序員用來使線程在特定條件下等待的技術。 Unlike traditional methods eg wait(), sleep() or yield() which all involves relinquishing CPU control, this method does not relinquish CPU, instead it just runs empty loop. Why would someone do that? to preserve CPU caches. In multi core system, its possible for a paused thread to resume on different core, which means rebuilding cache again. To avoid cost of rebuilding cache, programmer prefer to wait for much smaller time doing busy spin. You can also see this answer to learn more about this question.
This is an interesting question for Java programmer, at first, volatile and atomic variable look very similar, but they are different. Volatile variable provides you happens-before guarantee that a write will happen before any subsequent write, it doesn't guarantee atomicity. For example count++ operation will not become atomic just by declaring count variable as volatile. On the other hand AtomicInteger class provides atomic method to perform such compound operation atomically eg getAndIncrement() is atomic replacement of increment operator. It can be used to atomically increment current value by one. Similarly you have atomic version for other data type and reference variable as well.
This is one more tricky question for average Java programmer, if he can bring the fact about whether lock is released or not is key indicator of his understanding. To answer this question, no matter how you exist synchronized block, either normally by finishing execution or abruptly by throwing exception, thread releases the lock it acquired while entering that synchronized block. This is actually one of the reason I like synchronized block over lock interface, which requires explicit attention to release lock, generally this is achieved by releasing lock in finally block .
This is one of the very popular question on Java interviews, and despite its popularity, chances of candidate answering this question satisfactory is only 50%. Half of the time, they failed to write code for double checked locking and half of the time they failed how it was broken and fixed on Java 1.5. This is actually an old way of creating thread-safe singleton, which tries to optimize performance by only locking when Singleton instance is created first time, but because of complexity and the fact it was broken for JDK 1.4, I personally don't like it. Anyway, even if you not prefer this approach its good to know from interview point of view. Since this question deserve a detailed answer, I have answered in a separate post, you can read my post how double checked locking on Singleton works to learn more about it.
This question is actually follow-up of previous question. If you say you don't like double checked locking then Interviewer is bound to ask about alternative ways of creating thread-safe Singleton class. There are actually man, you can take advantage of class loading and static variable initialization feature of JVM to create instance of Singleton, or you can leverage powerful enumeration type in Java to create Singleton. I actually preferred that way, you can also read this article to learn more about it and see some sample code.
This is my favourite question, because I believe that you must follow certain best practices while writing concurrent code which helps in performance, debugging and maintenance. Following are three best practices, I think an average Java programmer should follow:
- Always give meaningful name to your thread This goes a long way to find a bug or trace an execution in concurrent code. OrderProcessor, QuoteProcessor or TradeProcessor is much better than Thread-1. Thread-2 and Thread-3. Name should say about task done by that thread. All major framework and even JDK follow this best practice.
- Avoid locking or Reduce scope of Synchronization
Locking is costly and context switching is even more costlier. Try to avoid synchronization and locking as much as possible and at bare minimum, you should reduce critical section. That's why I prefer synchronized block over synchronized method, because it gives you absolute control on scope of locking. - Prefer Synchronizers over wait and notify
Synchronizers like CountDownLatch, Semaphore, CyclicBarrier or Exchanger simplifies coding. It's very difficult to implement complex control flow right using wait and notify. Secondly, these classes are written and maintained by best in business and there is good chance that they are optimized or replaced by better performance code in subsequent JDK releases. By using higher level synchronization utilities, you automatically get all these benefits. - Prefer Concurrent Collection over Synchronized Collection
This is another simple best practice which is easy to follow but reap good benefits. Concurrent collection are more scalable than their synchronized counterpart, that's why its better to use them while writing concurrent code. So next time if you need map, think about ConcurrentHashMap before thinking Hashtable. See my article Concurrent Collections in Java , to learn more about modern collection classes and how to make best use of them.
This question is like how do you force garbage collection in Java, their is no way, though you can make request using System.gc() but its not guaranteed. On Java multi-threading their is absolute no way to force start a thread, this is controlled by thread scheduler and Java exposes no API to control thread schedule. This is still a random bit in Java.
The fork join framework, introduced in JDK 7 is a powerful tool available to Java developer to take advantage of multiple processors of modern day servers. 它是為可以遞歸分解為較小部分的工作而設計的。 目標是使用所有可用的處理能力來增強應用程序的性能。 One significant advantage of The fork/join framework is that it uses a work-stealing algorithm. 工作用盡的工作線程可以從其他仍很忙的線程中竊取任務。 See this article for much more detailed answer of this question.
Though both wait and sleep introduce some form of pause in Java application, they are tool for different needs. Wait method is used for inter thread communication, it relinquish lock if waiting condition is true and wait for notification when due to action of another thread waiting condition becomes false. On the other hand sleep() method is just to relinquish CPU or stop execution of current thread for specified time duration. Calling sleep method doesn't release the lock held by current thread. You can also take look at this article to answer this question with more details.
That's all on this list of top 50 Java multi-threading and concurrency interview questions . I have not shared answers of all the questions but provided enough hints and links to explore further and find answers by yourselves. As I said, let me know if you don't find answer of any particular question and I will add answer here. You can use this list to not only to prepare for your core Java and programming interviews but also to check your knowledge about basics of threads, multi-threading, concurrency, design patterns and threading issues like race conditions, deadlock and thread safety problems. My intention is to make this list of question as mother of all list of Java Multi-threading questions, but this can not be done without your help. You can also share any question with us, which has been asked to you or any question for which you yet to find an answer. This master list is equally useful to Java developers of all levels of experience. You can read through this list even if you have 2 to 3 years of working experience as junior developer or 5 to 6 years as senior developer. It's even useful for freshers and beginners to expand their knowledge. I will add new and latest multi-threading question as and when I come across, and I request you all to ask, share and answer questions via comments to keep this list relevant for all Java programmers.
翻譯自: https://www.javacodegeeks.com/2014/07/top-50-java-thread-interview-questions-answers-for-freshers-experienced-programmers.html
總結
以上是生活随笔為你收集整理的Java线程面试的前50个问题,面向初学者和经验丰富的程序员的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Spring MVC集成测试:断言给定的
- 下一篇: 低端手机性价比排行(性价比最低手机排行榜