跳到主要內容

Java多線程與併發基礎面試題


CS-LogN思維導圖:記錄專業基礎 面試題
開源地址:https://github.com/FISHers6/CS-LogN




多線程與併發基礎


實現多線程


面試題1:有幾種實現線程的方法,分別是什麼



  • 1.繼承Thread類,啟動線程的唯一方法就是通過 Thread 類的 start()實例方法,start()方法是一個 native 方法,它將啟動一個新線程去執行 run()方法


  • 2.實現 Runnable 接口,重寫run()函數,作為參數放到Thread類構造函數中作為target屬性,運行start()方法


  • 線程池創建線程、Callable本質還是使Runnable創建,Callable是父輩類繼承了Runnable,線程池需傳入參數






面試題2:實現Runnable方法好,還是繼承Thread類好



  • 實現Runnable接口更好



    • 1.單一繼承原則,如果繼承了Thread,就不能繼承其它類了,限制了可擴展性

    • 2.Thread類每次只能創建一個獨立的線程,損耗大,而Runnable能利用線程池工具來創建線程

    • 3.從代碼架構上看,run內容應該與Trhead代碼解耦



面試題3:一個線程兩次調用start方法會出現什麼情況(考察源碼)



  • 第二次會出現異常,從start源碼上和線程生命周期上分析,一個線程start后,
    改變了threadState狀態字;而第二次再start每次會先檢查這個狀態不是0就報異常


面試題4:既然start方法會調用run方法,為什麼我們還是要用start方法,而不是直接調用run方法呢(考察源碼)



  • 因為start后線程才會經過完整的線程生命周期,start調用native start0,虛擬機執startThread,thread_entry入口中調用Thread的run,


面試題5:start和run有什麼區別



  • run()方法:只是普通的方法,調用run普通方法,可以重複多次調用

  • start()方法,會啟動一個線程,使得虛擬機去調用Runnable對象的run()方法,不能多次啟動同一個線程


面試題6:start方法如何調用run方法的(考察源碼和JVM)



  • start方法調用native start0,JVM虛擬機執行startThread,在thread_entry中調用Thread的run方法


面試題7:如何正確停止線程



  • 使用interrupt中斷通知,而不是強制,中斷通知後會讓被停止線程去決定何時停止,即把主動權交給需要被中斷的線程


線程的生命周期


面試題1:Java線程有哪幾種狀態 說說生命周期



  • 六種生命狀態(若time_waiting也算一種)



    • New,已創建但還尚未啟動的新線程

    • Runable,可運行狀態;對應操作系統的兩種狀態"就緒態" 和 "運行態"(分配到CPU)

    • Blocked,阻塞狀態;請求synchronized鎖未分配到時阻塞,直到獲取到monitor鎖再進入Runnable

    • Waiting,等待狀態

    • Timed waiting,限期等待

    • Terminated終止狀態


  • 線程的生命周期 狀態轉換圖






Thread和Object類中


與線程相關的重要方法


面試題1:實現兩個線程交替打印奇數偶數


面試題2:手寫生產者消費者設計模式,為什麼用該模式



  • 主要是為了解耦,匹配不同的能力


面試題3:wait后發生了什麼,為什麼需要在同步代碼內才能使用



  • 從jvm的源碼實現上看,wait后,線程讓出佔有的cpu並釋放同步資源鎖;把自己加入到等待池,以後不會再主動參与cpu的競爭,除非被其它notify命中

  • 為了確保線程安全;另外wait會釋放資源,所以肯定要先拿到這個鎖,能進入同步代碼塊已經拿到了鎖


面試題4:為什麼線程通信的方法wait,notify和notifyAll放在Object類,而sleep定義在Thread類里 (考察對象鎖)



  • 與對象的鎖有關,對象鎖綁定在對象的對象頭中,且放在Object里,使每個線程都可以持有多個對象的鎖


面試題5:wait方法是屬於Object對象的,那調用Thread.wait會怎麼樣



  • 線程死的時候會自己notifyAll,釋放掉所有的持有自己對象的鎖。這個機制是實現很多同步方法的基礎。如果調用Thrad.wait,干擾了我們設計的同步業務流程


面試題6:如何選擇notify還是notifyAll



  • 優先選用notifyAll,喚醒所有線程;除非業務需要每次只喚醒一個線程的


面試題7:notfiy后發生的操作,notifyAll之後所有的線程都會再次搶奪鎖,如果某線程搶奪失敗怎麼辦?



  • notify后,讓waiterSet等待池中的一個線程與entry_List鎖池一級活躍線程一起競爭CPU

  • 搶奪鎖失敗後會繼續待在原鎖池或原等待池,等待競爭CPU的調度


面試題8:sleep方法與notify/wait方法的異同點



  • 相同點:線程都會進入waiting狀態,都可以響應中斷

  • 不同點:1.所屬類不同;2.wait/notify必須用在同步方法中,且會釋放鎖;3.sleep可以指定時間


面試題9:join方法後父線程進入什麼狀態



  • waiting狀態,join內部調用wait,子線程結束后自動調用notifyAll喚醒(jvm:exit函數)


線程安全與性能


面試題1:守護線程和普通線程的區別



  • 守護線程是服務於普通線程的,並且不會影響到jvm的退出


面試題2:什麼是線程安全



  • 不管業務中遇到怎樣的多個線程訪問某對象或某方法的情況,而在編程這個業務邏輯的時候,都不需要再額外做任何額外的處理(也就是可以像單線程編程一樣),程序也可以正常運行(不會因為多線程而出錯),就可以稱為線程安全


面試題3:有哪些線程不安全的情況,什麼原因導致的



  • 1.數據爭用、同時操作,如數據讀寫由於同時寫,非原子性操作導致運行結果錯誤,a++

  • 2.存在競爭,順序不當,如死鎖、活鎖、飢餓


面試題4:什麼是多線程的上下文切換,及導致的後果



  • 進程線程切換要保存所需要的CPU運行環境,如寄存器、棧、全局變量等資源

  • 在頻繁的io以及搶鎖的時候,會導緻密集的上下文切換,多線程切換時,由於緩存和上下文的切換會帶來性能問題


面試題5:多線程導致的開銷有哪些



  • 1.上下文切換開銷,如保存緩存(cache、快表等)的開銷


  • 2.同步協作的開銷(java內存模型)



    • 為了數據的正確性,同步手段往往會使用禁止編譯器優化(如指令重排序優化、鎖粗化等),性能變差

    • 使CPU內的緩存失效(比如volatile可見性讓自己線程的緩存失效后,必須使用主存來查看數據)



Java內存模型


面試題1:Java的代碼如何一步步轉化,最終被CPU執行的






    1. 最開始,我們編寫的Java代碼,是*.java文件




  1. 在編譯(javac命令)后,從剛才的.java文件會變出一個新的Java字節碼文件.class

  2. JVM會執行剛才生成的字節碼文件(*.class),並把字節碼文件轉化為機器指令

  3. 機器指令可以直接在CPU上執運行,也就是最終的程序執行



  • JVM實現會帶來不同的"翻譯",不同的CPU平台的機器指令又千差萬別,無法保證併發安全的效果一致


面試題2:單例模式的作用和適用場景



  • 單例模式:只獲取一次資源,全程序通用,節省內存和計算;保證多線程計算結果正確;方便管理;
    比如日期工具類只需要一個實例就可以,無需多個示例


面試題3:單例模式的寫法,考察(重排序、單例和高併發的關係)



  • 餓漢式(靜態常量、靜態代碼塊)



    • 原理1:static靜態常量在類加載的時候就初始化完成了,且由jvm保證線程安全,保證了變量唯一

    • 原理2:靜態代碼塊中實例化和靜態常量類似;放在靜態代碼塊里初始化,類加載時完成;

    • 特徵:簡單,但沒有懶加載(需要時再加載)


  • 懶漢式(加synchronized鎖)



    • 對初始化的方法加synchronized鎖達到線程安全的目的,但效率低,多線程下變成了同步

    • 懶漢式取名:用到的時候才去加載


  • 雙重檢查



    • 代碼實現



      • 屬性加volatile,兩次if判斷NULL值,第二次前加類鎖


    • 優點



      • 線程安全;延遲加載;效率高


    • 為什麼用雙重而不用單層



      • 從線程安全方面、效率方面講



  • 靜態內部類



    • 需要理解靜態內部類的優點,懶漢式加載,jvm加載順序


  • 枚舉



    • 代碼實現簡單



      • public enum Singleton{
        INSTANCE;
        public void method(){}
        }


    • 保證了線程安全



      • 枚舉是一個特殊的類,經過反編譯查看,枚舉最終被編譯成一個final的類,繼承了枚舉父類。各個實例通過static定義,本質就是一個靜態的對象,所有第一次使用的時候採取加載(懶加載)


    • 避免反序列化破壞單例



      • 避免了:比如用反射就繞過了構造方法,反序列化出多個實例




面試題4:單例模式各種寫法分別適用的場合



  • 1.最好的方法是枚舉,因枚舉被編譯成final類,用static定義靜態對象,懶加載。既保證了線程安全又避免了反序列化破壞單例

  • 2.如果程序一開始要加載的資源太多,考慮到啟動速度,就應該使用懶加載

  • 3.如果是對象的創建需要配置文件(一開始要加載其它資源),就不適合用餓漢式


面試題5:餓漢式單例的缺點



  • 沒有懶加載(初始化時全部加載出),初始化開銷大


面試題6:懶漢式單例的缺點



  • 雖然用到的時候才去加載,但是由於加鎖,性能低


面試題7:單例模式的雙重檢查寫法為什麼要用double-check



  • 從代碼實現出發,保證線程安全、延遲加載效率高


面試題8:為什麼雙重檢查要用volatile



  • 1.保證instance的可見性



    • 類初始化分成3條指令,重排序帶來NPE空虛指針問題,加volatile防止重排序


  • 2.防止初始化指令重排序



面試題9:講一講什麼是Java的內存模型



  • 1.是一組規範,需要JVM實現遵守這個規範,以便實現安全的多線程程序
    2.volatile、synchronized、Lock等同步工具和關鍵字實現原理都用到了JMM
    3.重排序、內存可見性、原子性


面試題10:什麼是happens-before,規則有哪些



  • 解決可見性問題的:在時間上,動作A發生在動作B之前,B保證能看見A,這就是happens-before


  • 規則



    • 1 單線程按代碼順序規則;2 鎖操作(synchronized和Lock);3volatile變量;4.JUC工具類的Happens-Before原則

    • 5.線程啟動時子線程啟動前能看到主線程run的所有內容;6.線程join主線程一定要等待子線程完成后再去做後面操作

    • 7.傳遞性 8.中斷檢測 9.對象構造方法的最後一行指令 happens-before 於 finalize() 方法的第一行指令



面試題11:講一講volatile關鍵字



  • volatile是一種同步機制,比synchronized或者Lock相關類更輕量,因為使用volatile並不會發生上下文切換等開銷很大的行為。而加鎖時對象鎖會阻塞開銷大。

  • 可見性,如果一個變量別修飾成volatile,那麼JVM就知道了這個變量可能會被併發修改;

  • 不能保證原子性


面試題12:volatile的適用場合及作用



  • 作用



    • 1.保證可見性 2.禁止指令重排序(單例雙重鎖時)


  • 適合場景



    • 適用場合1:boolean flag,布爾具有原子性,可再由volatile保證其可見性

    • 適用場合2:作為刷新之前變量的觸發器

    • 但不適合非原子性操作如:a++等



面試題13:volatile和synchronized的異同



  • 1 性能開銷方面: 鎖開銷更大,volatile無加鎖阻塞開銷
    2 作用方面:volatile只能保證可見性,鎖既能保證可見性,又能保證原子性


面試題14:什麼是內存可見性問題,為什麼存在



  • 多線程下,一個線程修改共享數據后,其它線程能否感知到修改了數據的線程的變化

  • CPU有多級緩存,導致讀的數據過期,各處理機有獨自的緩存未及時更新時,與主存內容不一致


面試題15:主內存和本地內存的關係是什麼



  • Java 作為高級語言,屏蔽了CPU cache等底層細節,用 JMM 定義了一套讀寫內存數據的規範,雖然我們不再需要關心一級緩存和二級緩存的問題,但是,JMM 抽象了主內存和本地內存的概念。

  • 線程擁有自己的本地內存,並共享主內存的數據;線程讀寫共享數據也是通過本地內存交換的,所以才導致了可見性問題。


面試題16:什麼是原子操作,Java的原子操作有哪些



  • 原子操作



    • 一系列的操作,要麼全部執行成功,要麼全部不執行,不會出現執行一半的情況,是不可分割的。


  • 1)除long和double之外的基本類型(int, byte, boolean, short, char, float)的"賦值操作"


  • 2)所有"引用reference的賦值操作",不管是 32 位的機器還是 64 位的機器


  • 3)java.concurrent.Atomic.* 包中所有類的原子操作



面試題17:long 和 double 的原子性你了解嗎



  • 在32位上的JVM上,long 和 double的操作不是原子的,但是在64位的JVM上是原子的。

  • 在32位機器上一次只能讀寫32位;而浮點數、long型有8字節64位;要分高32位和低32位兩條指令分開寫入,類似彙編語言浮點數乘法分高低位寄存器;64位不用分兩次讀寫了


面試題18:生成對象的過程是不是原子操作



  • 不是,對象生成會生成分配空間、初始化、賦值,三條指令,有可能會被重排序,導致空指針


面試題19:區分JVM內存結構、Java內存模型 、Java對象模型



  • Java內存模型,和Java的併發編程有關



    • 詳見面試題9


  • JVM內存結構,和Java虛擬機的運行時區域(堆棧)有關



    • 堆區、方法區(存放常量池 引用 類信息)
      棧區、本地方法棧、程序計數器






  • Java對象模型,和Java對象在虛擬機中的表現形式有關



    • 是Java對象自身的存儲模型,在方法區中Kclass類信息(虛函數表),在堆中存放new實例,在線程棧中存放引用,OOP-Klass Model



面試題20:什麼是重排序



  • 指令實際執行順序和代碼在java文件中的順序不一致

  • 重排序的好處:提高處理速度,包括編譯器優化、指令重排序(局部性原理)


死鎖


面試題1:寫一個必然死鎖的例子



  • synchronized嵌套,構成請求循環


面試題2:生產中什麼場景下會發生死鎖



  • 併發中多線程互不相讓:當兩個(或更多)線程(或進程)相互持有對方所需要的資源,又不主動釋放,導致所有人都無法繼續前進,導致程序陷入無盡的阻塞,這就是死鎖。


面試題3:發生死鎖必須滿足哪些條件



  • 1.互斥

  • 2.請求和保持

  • 3.不可剝奪

  • 4.存儲循環等待鏈


面試題4:如何用工具定位死鎖



  • 1.jstack命令在程序發生死鎖后,進行堆棧分析出死鎖線程

  • 2.ThreadMXbean 程序運行中發現死鎖,一旦發現死鎖可以讓用戶去打日誌


面試題5:有哪些解決死鎖問題的策略



  • 1.死鎖語法,不讓死鎖發生



    • 破壞死鎖的四個條件之一;如:哲學家換手、轉賬換序


  • 2.死鎖避免



    • 銀行家算法、系統安全序列


  • 3.死鎖檢查與恢復



    • 適用資源請求分配圖,一段時間內檢查死鎖,有死鎖就恢復策略,採用恢復策略;

    • 恢復方法:進程終止 、資源剝奪


  • 4.鴕鳥策略(忽略死鎖)



    • 先忽略,後期再讓人工恢復



面試題6:死鎖避免策略和檢測與恢復策略的主要思路是什麼



  • 死鎖語法



    • 破壞死鎖的四大條件之一


  • 死鎖避免



    • 找到安全序列,銀行家算法


  • 死鎖檢測與恢復



    • 資源請求分配圖



面試題7:講一講經典的哲學家就餐問題,如何解決死鎖



  • 什麼時候死鎖



    • 哲學家各拿起自己左手邊的筷子,又去請求拿右手邊筷子循環請求時而阻塞


  • 如何解決死鎖



    • 1.一次兩隻筷子,形成原子性操作

    • 2.只允許4個人拿有筷子



面試題8:實際開發中如何避免死鎖



  • 設置超時時間

  • 多使用併發類而不是自己設計鎖

  • 盡量降低鎖的使用粒度:用不同的鎖而不是一個鎖,鎖的範圍越小越好

  • 避免鎖的嵌套:MustDeadLock類

  • 分配資源前先看能不能收回來:銀行家算法

  • 盡量不要幾個功能用同一把鎖:專鎖專用

  • 給你的線程起個有意義的名字:debug和排查時事半功倍,框架和JDK都遵守這個最佳實踐


面試題9:什麼是活躍性問題?活鎖、飢餓和死鎖有什麼區別



  • 活鎖



    • 雖然線程並沒有阻塞,也始終在運行(所以叫做"活"鎖,線程是"活"的),但是程序卻得不到進展,因為線程始終互相謙讓,重複做同樣的事


    • 工程中的活鎖實例:消息隊列,消息如果處理失敗,就放在隊列開頭重試,沒阻塞程序無法繼續


    • 如何解決活鎖問題



      • 加入隨機因素,以太網的指數退避算法



  • 飢餓



    • 當線程需要某些資源(例如CPU),但是卻始終得不到,可能原因是飢餓線程的優先級過低


本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】



※為什麼 USB CONNECTOR 是電子產業重要的元件?



網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!



※台北網頁設計公司全省服務真心推薦



※想知道最厲害的網頁設計公司"嚨底家"!



※推薦評價好的iphone維修中心



Orignal From: Java多線程與併發基礎面試題

留言

這個網誌中的熱門文章

掃地機器人可以隨身帶上飛機嗎?我想要拿去送給國外的朋友

掃地機器人如果要隨身戴上飛機需要滿足兩個條件: 一個是掃地機器人連同你的隨身行李,整體的體積和重量要符合上機條件,這個具體每家航空公司都不同,可以諮詢,簡單的說就是隨身行李不要超寬超重。 還有一個就是由於掃地機器人內置了鋰電池,所以內置電池的容量要符合相關規定,每個掃地機器人電池容量都不同,具體自行查詢。 根據民航的相關安全要求,凡帶有鋰電池的電子設備均不可以托運,但符合重量要求,尺寸要求以及電量要求的鋰電池及其設備是可以帶上飛機的。 《鋰電池航空運輸規範》中內含鋰離子電池的設備電池額定能量不應超過100Wh的規定,符合國標GB31241-2014,並通過UN38.3航空運輸認證等國際安全標準,所以可以帶上飛機。但是不能托運,只能隨身攜帶。 掃地機器人     掃地機器人     掃地機器人吸塵器 http://www.greenh3y.com/?p=400 Orignal From: 掃地機器人可以隨身帶上飛機嗎?我想要拿去送給國外的朋友

不滿國際規範斷財路 非洲多國擬退野生動保公約

摘錄自2019年09月01日中央通訊社非洲報導 非洲南部多國揚言退出「瀕臨絕種野生動植物國際貿易公約」,因為公約多數成員拒絕放寬象牙與犀牛角交易,並且幾乎全面禁止將野生捕獲的大象送到動物園。 這個公約嚴格規範全球野生動物交易,包括限制象牙與犀牛角交易。 本週在日內瓦召開修訂「瀕臨絕種野生動植物國際貿易公約」(CITES)的會議期間,由於區域集團非南開發共同體(SADC)的多項提案遭否決,這個集團與公約的關係惡化。 全球大象數量最多的區域波札那、納米比亞與辛巴威要求販售取自自然死亡、充公與汰除的大象象牙,這項提議被居多數的101票否決。 40多年前制訂的CITES規範約3萬6000種動植物交易,並設計有助於遏止非法交易和制裁違規國家的機制。 不過有16個成員國的非南開發共同體部分會員批評CITES對非洲國家的問題視若無睹。 坦尚尼亞環境部長西蒙巴徹恩(George Simbachawene)於日內瓦召開的會議中表示:「結果無法採取進步、公平、包容與基於科學的的保育策略。」 他說:「該是認真重新考慮我們加入CITES是否有任何實質益處的時候了。」 本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理 【搬家相關資訊指南】 台中搬家 , 彰化搬家 , 南投搬家 前需注意的眉眉角角,別等搬了再說! 避免吃悶虧無故遭抬價! 台中搬家公司 免費估價,有契約讓您安心有保障! 評比 彰化搬家公司費用 , 南投搬家公司費用 收費行情懶人包大公開 彰化搬家費用 , 南投搬家費用 ,距離,噸數怎麼算?達人教你簡易估價知識! Orignal From: 不滿國際規範斷財路 非洲多國擬退野生動保公約

全球第一國 帛琉立法禁用、禁售防曬乳

摘錄自2018年11月2日蘋果日報帛琉報導 為了保護珊瑚礁生態,帛琉政府昨(1)日表示已立法嚴禁販售並使用防曬乳,此法將於2020年1月1日起正式生效。帛琉也成為全球首個全面禁止防曬乳的國家。 帛琉國會上周通過此法案,全面禁止使用和販售含有10種有害化學物質的防曬乳,違者將被處以1000美元(約3萬783元台幣)罰款。若遊客被發現私帶防曬乳入境,也會遭到沒收。帛琉總統雷蒙傑索(Tommy Remengesau)說:「沒收(防曬乳)已經足夠讓人不進行商業使用,而這也是很聰明的一招,一方面教育觀光客,又不會把他們嚇跑。」 根據官方說法,帛琉的熱門潛水點每小時會有4艘載著觀光客的船隻造訪,他們身上的防曬乳化學物質相當可觀。總統府發言人說:「帛琉各潛水和浮潛地點每天都有好幾加崙的防曬乳入海。我們只是盡力要防止環境遭受污染。」 本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理 【其他文章推薦】 ※ 台中搬家 , 彰化搬家 , 南投搬家 前需注意的眉眉角角,別等搬了再說! ※在找尋 搬家 公司嗎? ※搬家不受騙不被宰 桃園搬家公司 , 桃園市搬家公司 公開報價讓你比價不吃虧! Orignal From: 全球第一國 帛琉立法禁用、禁售防曬乳