Java程序員面試中的多線程問題

很多核心Java面試題來源于多線程(Multi-Threading)和集合框架(Collections Framework),理解核心線程概念時,嫻熟的實際經驗是必需的。這篇文章收集了 Java 線程方面一些典型的問題,這些問題經常被高級工程師所問到。

0.Java 中多線程同步是什么?

在多線程程序下,同步能控制對共享資源的訪問。如果沒有同步,當一個 Java 線程在修改一個共享變量時,另外一個線程正在使用或者更新同一個變量,這樣容易導致程序出現錯誤的結果。

1.解釋實現多線程的幾種方法?

一 Java 線程可以實現 Runnable 接口或者繼承 Thread 類來實現,當你打算多重繼承時,優先選擇實現 Runnable。

2.Thread.start ()與 Thread.run ()有什么區別?

Thread.start ()方法(native)啟動線程,使之進入就緒狀態,當 cpu 分配時間該線程時,由 JVM 調度執行 run ()方法。

3.為什么需要 run ()和 start ()方法,我們可以只用 run ()方法來完成任務嗎?

我們需要 run ()&start ()這兩個方法是因為 JVM 創建一個單獨的線程不同于普通方法的調用,所以這項工作由線程的 start 方法來完成,start 由本地方法實現,需要顯示地被調用,使用這倆個方法的另外一個好處是任何一個對象都可以作為線程運行,只要實現了 Runnable 接口,這就避免因繼承了 Thread 類而造成的 Java 的多繼承問題。

4.什么是 ThreadLocal 類,怎么使用它?

ThreadLocal 是一個線程級別的局部變量,并非“本地線程”。ThreadLocal 為每個使用該變量的線程提供了一個獨立的變量副本,每個線程修改副本時不影響其它線程對象的副本(譯者注)。

下面是線程局部變量(ThreadLocal variables)的關鍵點:

一個線程局部變量(ThreadLocal variables)為每個線程方便地提供了一個單獨的變量。

ThreadLocal 實例通常作為靜態的私有的(private static)字段出現在一個類中,這個類用來關聯一個線程。

當多個線程訪問 ThreadLocal 實例時,每個線程維護 ThreadLocal 提供的獨立的變量副本。

常用的使用可在 DAO 模式中見到,當 DAO 類作為一個單例類時,數據庫鏈接(connection)被每一個線程獨立的維護,互不影響。(基于線程的單例)

ThreadLocal 難于理解,下面這些引用連接有助于你更好的理解它。

《Good article on ThreadLocal on IBM DeveloperWorks?》、《理解 ThreadLocal》、《Managing data : Good example》、《Refer Java API Docs》

5.什么時候拋出 InvalidMonitorStateException 異常,為什么?

調用 wait ()/notify ()/notifyAll ()中的任何一個方法時,如果當前線程沒有獲得該對象的鎖,那么就會拋出 IllegalMonitorStateException 的異常(也就是說程序在沒有執行對象的任何同步塊或者同步方法時,仍然嘗試調用 wait ()/notify ()/notifyAll ()時)。由于該異常是 RuntimeExcpetion 的子類,所以該異常不一定要捕獲(盡管你可以捕獲只要你愿意).作為 RuntimeException,此類異常不會在 wait (),notify (),notifyAll ()的方法簽名提及。

6.Sleep ()、suspend ()和 wait ()之間有什么區別?

Thread.sleep ()使當前線程在指定的時間處于“非運行”(Not Runnable)狀態。線程一直持有對象的監視器。比如一個線程當前在一個同步塊或同步方法中,其它線程不能進入該塊或方法中。如果另一線程調用了 interrupt ()方法,它將喚醒那個“睡眠的”線程。

注意:sleep ()是一個靜態方法。這意味著只對當前線程有效,一個常見的錯誤是調用t.sleep (),(這里的t是一個不同于當前線程的線程)。即便是執行t.sleep (),也是當前線程進入睡眠,而不是t線程。t.suspend ()是過時的方法,使用 suspend ()導致線程進入停滯狀態,該線程會一直持有對象的監視器,suspend ()容易引起死鎖問題。

object.wait ()使當前線程出于“不可運行”狀態,和 sleep ()不同的是 wait 是 object 的方法而不是 thread。調用 object.wait ()時,線程先要獲取這個對象的對象鎖,當前線程必須在鎖對象保持同步,把當前線程添加到等待隊列中,隨后另一線程可以同步同一個對象鎖來調用 object.notify (),這樣將喚醒原來等待中的線程,然后釋放該鎖。基本上 wait ()/notify ()與 sleep ()/interrupt ()類似,只是前者需要獲取對象鎖。

7.在靜態方法上使用同步時會發生什么事?

同步靜態方法時會獲取該類的“Class”對象,所以當一個線程進入同步的靜態方法中時,線程監視器獲取類本身的對象鎖,其它線程不能進入這個類的任何靜態同步方法。它不像實例方法,因為多個線程可以同時訪問不同實例同步實例方法。

8.當一個同步方法已經執行,線程能夠調用對象上的非同步實例方法嗎?

可以,一個非同步方法總是可以被調用而不會有任何問題。實際上,Java 沒有為非同步方法做任何檢查,鎖對象僅僅在同步方法或者同步代碼塊中檢查。如果一個方法沒有聲明為同步,即使你在使用共享數據 Java 照樣會調用,而不會做檢查是否安全,所以在這種情況下要特別小心。一個方法是否聲明為同步取決于臨界區訪問(critial section access),如果方法不訪問臨界區(共享資源或者數據結構)就沒必要聲明為同步的。

下面有一個示例說明:Common 類有兩個方法 synchronizedMethod1()和 method1(),MyThread 類在獨立的線程中調用這兩個方法。

  1. public?class?Common?{ ?
  2. ? ?
  3. public?synchronized?void?synchronizedMethod1()?{ ?
  4. System.out.println?("synchronizedMethod1?called"); ?
  5. try?{ ?
  6. Thread.sleep?(1000); ?
  7. }?catch?(InterruptedException?e)?{ ?
  8. e.printStackTrace?(); ?
  9. } ?
  10. System.out.println?("synchronizedMethod1?done"); ?
  11. } ?
  12. public?void?method1()?{ ?
  13. System.out.println?("Method?1?called"); ?
  14. try?{ ?
  15. Thread.sleep?(1000); ?
  16. }?catch?(InterruptedException?e)?{ ?
  17. e.printStackTrace?(); ?
  18. } ?
  19. System.out.println?("Method?1?done"); ?
  20. } ?
  21. }?
  1. public?class?MyThread?extends?Thread?{ ?
  2. private?int?id?=?0; ?
  3. private?Common?common; ?
  4. ? ?
  5. public?MyThread?(String?name,?int?no,?Common?object)?{ ?
  6. super(name); ?
  7. common?=?object; ?
  8. id?=?no; ?
  9. } ?
  10. ? ?
  11. public?void?run?()?{ ?
  12. System.out.println?("Running?Thread"?+?this.getName?()); ?
  13. try?{ ?
  14. if?(id?==?0)?{ ?
  15. common.synchronizedMethod1(); ?
  16. }?else?{ ?
  17. common.method1(); ?
  18. } ?
  19. }?catch?(Exception?e)?{ ?
  20. e.printStackTrace?(); ?
  21. } ?
  22. } ?
  23. ? ?
  24. public?static?void?main?(String[]?args)?{ ?
  25. Common?c?=?new?Common?(); ?
  26. MyThread?t1?=?new?MyThread?("MyThread-1",?0,?c); ?
  27. MyThread?t2?=?new?MyThread?("MyThread-2",?1,?c); ?
  28. t1.start?(); ?
  29. t2.start?(); ?
  30. } ?
  31. } ?

這里是程序的輸出:

  1. Running?ThreadMyThread-1 ?
  2. synchronizedMethod1?called ?
  3. Running?ThreadMyThread-2 ?
  4. Method?1?called ?
  5. synchronizedMethod1?done ?
  6. Method?1?done?

?

結果表明即使 synchronizedMethod1()方法執行了,method1()也會被調用。

9.在一個對象上兩個線程可以調用兩個不同的同步實例方法嗎?

不能,因為一個對象已經同步了實例方法,線程獲取了對象的對象鎖。所以只有執行完該方法釋放對象鎖后才能執行其它同步方法。看下面代碼示例非常清晰:Common 類有 synchronizedMethod1()和 synchronizedMethod2()方法,MyThread 調用這兩個方法。

  1. public?class?Common?{ ?
  2. public?synchronized?void?synchronizedMethod1()?{ ?
  3. System.out.println?("synchronizedMethod1?called"); ?
  4. try?{ ?
  5. Thread.sleep?(1000); ?
  6. }?catch?(InterruptedException?e)?{ ?
  7. e.printStackTrace?(); ?
  8. } ?
  9. System.out.println?("synchronizedMethod1?done"); ?
  10. } ?
  11. ? ?
  12. public?synchronized?void?synchronizedMethod2()?{ ?
  13. System.out.println?("synchronizedMethod2?called"); ?
  14. try?{ ?
  15. Thread.sleep?(1000); ?
  16. }?catch?(InterruptedException?e)?{ ?
  17. e.printStackTrace?(); ?
  18. } ?
  19. System.out.println?("synchronizedMethod2?done"); ?
  20. } ?
  21. }?
  1. public?class?MyThread?extends?Thread?{ ?
  2. private?int?id?=?0; ?
  3. private?Common?common; ?
  4. ? ?
  5. public?MyThread?(String?name,?int?no,?Common?object)?{ ?
  6. super(name); ?
  7. common?=?object; ?
  8. id?=?no; ?
  9. } ?
  10. ? ?
  11. public?void?run?()?{ ?
  12. System.out.println?("Running?Thread"?+?this.getName?()); ?
  13. try?{ ?
  14. if?(id?==?0)?{ ?
  15. common.synchronizedMethod1(); ?
  16. }?else?{ ?
  17. common.synchronizedMethod2(); ?
  18. } ?
  19. }?catch?(Exception?e)?{ ?
  20. e.printStackTrace?(); ?
  21. } ?
  22. } ?
  23. ? ?
  24. public?static?void?main?(String[]?args)?{ ?
  25. Common?c?=?new?Common?(); ?
  26. MyThread?t1?=?new?MyThread?("MyThread-1",?0,?c); ?
  27. MyThread?t2?=?new?MyThread?("MyThread-2",?1,?c); ?
  28. t1.start?(); ?
  29. t2.start?(); ?
  30. } ?
  31. }?

10.什么是死鎖

死鎖就是兩個或兩個以上的線程被無限的阻塞,線程之間相互等待所需資源。這種情況可能發生在當兩個線程嘗試獲取其它資源的鎖,而每個線程又陷入無限等待其它資源鎖的釋放,除非一個用戶進程被終止。就 JavaAPI 而言,線程死鎖可能發生在一下情況。

  • 當兩個線程相互調用 Thread.join ()
  • 當兩個線程使用嵌套的同步塊,一個線程占用了另外一個線程必需的鎖,互相等待時被阻塞就有可能出現死鎖。

11.什么是線程餓死,什么是活鎖?

線程餓死和活鎖雖然不想是死鎖一樣的常見問題,但是對于并發編程的設計者來說就像一次邂逅一樣。

當所有線程阻塞,或者由于需要的資源無效而不能處理,不存在非阻塞線程使資源可用。JavaAPI 中線程活鎖可能發生在以下情形:

  • 當所有線程在程序中執行 Object.wait (0),參數為 0 的 wait 方法。程序將發生活鎖直到在相應的對象上有線程調用 Object.notify ()或者 Object.notifyAll ()。
  • 當所有線程卡在無限循環中。

這里的問題并不詳盡,我相信還有很多重要的問題并未提及,您認為還有哪些問題應該包括在上面呢?歡迎在評論中分享任何形式的問題與建議。

譯文出自:jobbole

英文出自:fromdev


本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/451061.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/451061.shtml
英文地址,請注明出處:http://en.pswp.cn/news/451061.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

SpringBoot2使用WebFlux函數式編程

本文只是簡單使用SpringBoot2使用WebFlux的函數式編程簡單使用,后續會繼續寫關于Webflux相關的文章。 最近一直在研究WebFlux,后續會陸續出一些相關的文章。 首先看一下Srping官網上的一張圖,對比一下SpringMvc和Spring WebFlux,如…

單點登錄原理與簡單實現

前些天發現了一個巨牛的人工智能學習網站,通俗易懂,風趣幽默,忍不住分享一下給大家。點擊跳轉到教程。 一、單系統登錄機制 1、http無狀態協議 web應用采用browser/server架構,http作為通信協議。http是無狀態協議,瀏…

java接口類支持多繼承

一個類只能extends一個父類,只能有一個父類,但可以implements多個接口。java通過使用接口的概念來取代C中多繼承。與此同時,一個接口則可以同時extends多個接口,卻不能implements任何接口。Java中的接口是支持多繼承的。

xmind-HTTP協議

轉載于:https://www.cnblogs.com/margot921/p/9764788.html

彈性布局

彈性布局 一、Flex布局是什么? Flex是Flexible Box的縮寫,意為”彈性布局”,用來為盒狀模型提供最大的靈活性。任何一個容器都可以指定為Flex布局。 二、基本概念 采用Flex布局的元素,稱為Flex容器(flex container&…

Java-Type簡單分類

&#xff08;1&#xff09;ParameterizedType&#xff1a; 參數化類型&#xff0c;例如List<T>。 &#xff08;2&#xff09;GenericArrayType&#xff1a; 泛型數組類型&#xff0c;例如T[]。 &#xff08;3&#xff09;TypeVariable&#xff1a; 泛型的類型變量&a…

解決dataTable 報錯:cannot read property “style“ of undefined

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 其實這錯&#xff0c;我之前也遇到過&#xff0c;只是太久了&#xff0c;沒有記錄下來&#xff0c; 今天看到群里朋友也遇到這個錯誤&a…

萬惡之源 - Python基礎數據類型一

整數 整數在Python中的關鍵字用int來表示; 整型在計算機中運于計算和比較 在32位機器上int的范圍是: -2**31&#xff5e;2**31-1&#xff0c;即-2147483648&#xff5e;2147483647 在64位機器上int的范圍是: -2**63&#xff5e;2**63-1&#xff0c;即-9223372036854775808&…

談談對于技術面試的心得體驗

導讀&#xff1a;作者lzprgmr寫了一篇《談談技術面試》文章&#xff0c;他在文中講述了自己對于技術人員面試的經驗和心得&#xff0c;以下是文章內容&#xff1a; 只要是招一個技術人員&#xff0c;不管是初級的程序員還是高級軟件工程師&#xff0c;技術上的考核都必不可少。…

es6中class類的全方面理解(三)------靜態方法

不需要實例化類&#xff0c;即可直接通過該類來調用的方法&#xff0c;即稱之為“靜態方法”。將類中的方法設為靜態方法也很簡單&#xff0c;在方法前加上static關鍵字即可。這樣該方法就不會被實例繼承&#xff01; class Box{static a(){return "我是Box類中的&#xf…

jackson/fastJson boolean類型問題

1.我們以Person對象舉個栗子&#xff0c;person有三個屬性。name&#xff0c;age和isGay Data public class Person {public Person(String name, int age, boolean isGay) {this.name name;this.age age;this.isGay isGay;}private String name;private Integer age;priva…

django模板系統(下)

主要內容&#xff1a;母版&#xff0c;繼承母版&#xff0c;塊&#xff0c;組件&#xff0c;靜態文件 母版 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"x-ua-compatible" conten…

狗窩里的小日子 ...

來&#xff0c;把平時作的菜菜整理下下&#xff1a; 1. 2. 3. 4. 5. 6. 7. 8.

面試開發人員的有效方法

伯樂在線 寫道 "Alan Skorkin是一名軟件開發人員&#xff0c;這是他分享的另一篇有關面試和開發人員的文章(中文)。Skorkin 認為&#xff0c;“當要雇傭開發者時&#xff0c;傳統的面試方法顯得力不從心&#xff0c;這是必須要面對的現實。為什么不行&#xff1f;原因也許…

Android直接用手機打包apk!

你沒有看錯&#xff0c;用手機瀏覽器訪問Jenkins&#xff0c;就可以打包apk&#xff0c;并生成下載二維碼&#xff0c;發送郵件通知測試人員下載&#xff0c;從此解放雙手&#xff0c;告別打包測試。先上本人手機郵箱收到的打包成功通知效果圖&#xff1a; 廢話少說&#xff0c…

java中byte、short、char、boolean實際都是按照int處理的!

byte、char、short、boolean四種類型在匯編期或運行期間采取和int類型一樣的存儲方式&#xff0c;在計算時會先轉換為int類型&#xff0c;后進行計算。所以兩個short類型數據做算數運算&#xff0c;結果卻為int類型。這主要是因為jvm的字節碼為了簡潔高效&#xff0c;設計時只使…

4、2 核心組件

1、Stage&#xff1a;虛的  一組RDD構成的鏈條并行的task集合&#xff0c;同一Stage的所有任務有著相同的Shuffle依賴。階段的劃分按照shuffle標記來進行的。一個階段含多個RDD&#xff0c;先有RDD后有Stage一個階段含多個taskstage通過ShuffleDependency劃分&#xff0c;一個…

狗窩里的小日子- 2 ...

來&#xff0c;把平時作的菜菜整理下&#xff1a; 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20.

優秀程序員必備素質——快速調試

你是否有過這些經歷&#xff1a; 1.代碼敲完了&#xff0c;剛想松口氣&#xff0c;一運行程序&#xff0c;滿滿的Bug。 2.找啊找啊找&#xff0c;怎么找都找不到哪里出了問題。 3.調試了半天出不來&#xff0c;就開始便得心煩氣躁。 4.一天連一個Bug也沒調出來&#xff0c;…

Java程序編譯運行過程

整體流程 1.首先由源程序文件編譯成class文件。注意這里的源程序并不僅限于java程序&#xff0c;其他語言如果能夠編譯成class文件&#xff0c;并且符合jvm規范也能夠在jvm上運行。 2.jvm將class文件拷貝到內存&#xff0c;解釋成相應的機器語言運行。我們常用的hotspot虛擬機…