每天十道面試題-20200401

題目

  • 1、在 java 中守護線程和本地線程區別?
  • 2、線程與進程的區別?
  • 3、什麼是多線程中的上下文切換?
  • 4、死鎖與活鎖的區別,死鎖與飢餓的區別?
  • 5、Java 中用到的線程調度算法是什麼?
  • 6、什麼是線程組,爲什麼在 Java 中不推薦使用?
  • 7、爲什麼使用 Executor 框架?
  • 8、在 Java 中 Executor 和 Executors 的區別?
  • 9、如何在 Windows 和 Linux 上查找哪個線程使用的 CPU 時間最長?
  • 10、什麼是原子操作?在 Java Concurrency API 中有哪些原子類(atomic classes)?

解答

題目一
  • 題幹:在 java 中守護線程和本地線程區別?
  • 分析:
  • 有時候我們需要創建一個線程來執行一些輔助操作,但是又不希望這個線程阻礙JVM的關閉,所以此時我們就需要創建守護線程。
    線程分爲兩種:守護線程和普通線程,在JVM啓動時創建的所有線程中,除了主線程之外的其他所有線程都是守護線程,當創建一個新線程時,新線程會繼承創建他的線程的守護狀態。因爲默認狀態下主線程創建的所有線程都是普通線程。
    區別:普通線程和守護線程的區別在於當線程退出時發生的操作,當一個線程退出時,JVM會檢查其他正在運行的線程,如果這些線程都是守護線程,那麼JVM會正常退出操作。JVM停止時所有仍然存在的守護線程都將被拋棄,意味着即不會執行finally代碼塊,也不會執行回捲棧,而JVM則是直接退出。(垃圾回收線程就是輔助線程。)

  • 回答:
  • 區別:普通線程都是主線程創建的,此外我們也可以通過方法Thread.setDaemon(bool on);true則把該線程設置爲守護線程,反之則爲用戶線程。Thread.setDaemon()必須在Thread.start()之前調用,否則運行時會拋出異常。普通線程和守護線程的區別在於當線程退出時發生的操作,當一個線程退出時,JVM會檢查其他正在運行的線程,如果這些線程都是守護線程,那麼JVM會正常退出操作。JVM停止時所有仍然存在的守護線程都將被拋棄。

題目二
  • 題幹:線程與進程的區別?
  • 分析:
  • 進程是操作系統分配資源的最小單元,線程是操作系統調度的最小單元。
    一個程序至少有一個進程,一個進程至少有一個線程。

  • 回答:
  • L進程是操作系統分配資源的最小單元,線程是操作系統調度的最小單元。
    一個程序至少有一個進程,一個進程至少有一個線程。

題目三
  • 題幹:什麼是多線程中的上下文切換?
  • 分析:
  • 多線程會共同使用一組計算機上的CPU,而線程數大於給程序分配的CPU數量時,爲了讓各個線程都有執行的機會,就需要輪轉使用CPU。不同的線程切換使用CPU發生的切換數據等就是上下文切換。

  • 回答:
  • 多線程會共同使用一組計算機上的CPU,而線程數大於給程序分配的CPU數量時,爲了讓各個線程都有執行的機會,就需要輪轉使用CPU。不同的線程切換使用CPU發生的切換數據等就是上下文切換。

題目四
  • 題幹:死鎖與活鎖的區別,死鎖與飢餓的區別?
  • 分析:
  • 死鎖:是指兩個或兩個以上的進程(或線程)在執行過程中,因爭奪資源而造成的一種互相等待的現象,若無外力作用,它們都將無法推進下去。
    產生死鎖的必要條件:
    互斥條件:所謂互斥就是進程在某一時間內獨佔資源。
    請求與保持條件:一個進程因請求資源而阻塞時,對已獲得的資源保持不放。
    不剝奪條件:進程已獲得資源,在末使用完之前,不能強行剝奪。
    循環等待條件:若干進程之間形成一種頭尾相接的循環等待資源關係。
    活鎖:任務或者執行者沒有被阻塞,由於某些條件沒有滿足,導致一直重複嘗試,失敗,嘗試,失敗。
    活鎖和死鎖的區別在於,處於活鎖的實體是在不斷的改變狀態,所謂的“活”, 而處於死鎖的實體表現爲等待;活鎖有可能自行解開,死鎖則不能。
    飢餓:一個或者多個線程因爲種種原因無法獲得所需要的資源,導致一直無法執行的狀態。
    Java中導致飢餓的原因:
    高優先級線程吞噬所有的低優先級線程的CPU時間。
    線程被永久堵塞在一個等待進入同步塊的狀態,因爲其他線程總是能在它之前持續地對該同步塊進行訪問。
    線程在等待一個本身也處於永久等待完成的對象(比如調用這個對象的wait方法),因爲其他線程總是被持續地獲得喚醒。。

  • 回答:
  • 見分析。

題目五
  • 題幹:Java 中用到的線程調度算法是什麼?

  • 分析:

  • 採用時間片輪轉的方式。可以設置線程的優先級,會映射到下層的系統上面的優先級上,如非特別需要,儘量不要用,防止線程飢餓。

  • 回答:

  • 採用時間片輪轉的方式。可以設置線程的優先級,會映射到下層的系統上面的優先級上,如非特別需要,儘量不要用,防止線程飢餓。

題目六
  • 題幹:什麼是線程組,爲什麼在 Java 中不推薦使用?
  • 分析:
  • ThreadGroup類,可以把線程歸屬到某一個線程組中,線程組中可以有線程對象,也可以有線程組,組中還可以有線程,這樣的組織結構有點類似於樹的形式。
    線程組ThreadGroup對象中的stop,resume,suspend會導致安全問題,主要是死鎖問題,已經被官方廢棄,多以價值已經大不如以前。
    線程組ThreadGroup不是線程安全的,在使用過程中不能及時獲取安全的信息。

  • 回答:
  • ThreadGroup類,可以把線程歸屬到某一個線程組中,線程組中可以有線程對象,也可以有線程組,組中還可以有線程,這樣的組織結構有點類似於樹的形式。建議使用線程池。

題目七
  • 題幹:爲什麼使用 Executor 框架?
  • 分析:
  • · 每次執行任務創建線程 new Thread()比較消耗性能,創建一個線程是比較耗時、耗資源的。
    · 調用 new Thread()創建的線程缺乏管理,被稱爲野線程,而且可以無限制的創建,線程之間的相互競爭會導致過多佔用系統資源而導致系統癱瘓,還有線程之間的頻繁交替也會消耗很多系統資源。
    · 使用new Thread() 啓動的線程不利於擴展,比如定時執行、定期執行、定時定期執行、線程中斷等都不便實現。

  • 回答:
  • · 每次執行任務創建線程 new Thread()比較消耗性能,創建一個線程是比較耗時、耗資源的。
    · 調用 new Thread()創建的線程缺乏管理,被稱爲野線程,而且可以無限制的創建,線程之間的相互競爭會導致過多佔用系統資源而導致系統癱瘓,還有線程之間的頻繁交替也會消耗很多系統資源。
    · 使用new Thread() 啓動的線程不利於擴展,比如定時執行、定期執行、定時定期執行、線程中斷等都不便實現。

題目八
  • 題幹:在 Java 中 Executor 和 Executors 的區別?
  • 分析:
  • Executors 工具類的不同方法按照我們的需求創建了不同的線程池,來滿足業務的需求。
    Executor 接口對象能執行我們的線程任務。
    ExecutorService接口繼承了Executor接口並進行了擴展,提供了更多的方法我們能獲得任務執行的狀態並且可以獲取任務的返回值。
    使用ThreadPoolExecutor 可以創建自定義線程池。
    Future 表示異步計算的結果,他提供了檢查計算是否完成的方法,以等待計算的完成,並可以使用get()方法獲取計算的結果。
    Executors 中方法返回的是ExecutorService 而AbstractExecutorService implements ExecutorService
    ThreadPoolExecutor extends AbstractExecutorService
    一般建議直接使用ThreadPoolExecutor 的有參構造方法來創建線程池。

  • 回答:
  • 見分析

題目九
  • 題幹:如何在 Windows 和 Linux 上查找哪個線程使用的 CPU 時間最長?
  • 分析:
  • 1、 終端執行top命令,找出cpu耗用厲害的進程pid, 終端執行top命令,然後按下shift+p 查找出cpu利用最厲害的pid號
    2、 根據上面第一步拿到的pid號,top -H -p pid 。然後按下shift+p,查找出cpu利用率最厲害的線程號
    3、將獲取到的線程號轉換成16進制,轉換一下
    4、使用jstack工具將進程信息打印輸出
    5、 編輯/tmp/t.dat文件,查找線程號對應的信息

  • 回答:
  • 見分析

題目十
  • 題幹:什麼是原子操作?在 Java Concurrency API 中有哪些原子類(atomic classes)?
  • 分析:
  • 原子操作(atomic operation)意爲”不可被中斷的一個或一系列操作” 。
    處理器使用基於對緩存加鎖或總線加鎖的方式來實現多處理器之間的原子操作。
    在Java中可以通過鎖和循環CAS的方式來實現原子操作。 CAS操作——Compare & Set,或是 Compare & Swap,現在幾乎所有的CPU指令都支持CAS的原子操作。
    原子操作是指一個不受其他操作影響的操作任務單元。原子操作是在多線程環境下避免數據不一致必須的手段。
    int++並不是一個原子操作,所以當一個線程讀取它的值並加1時,另外一個線程有可能會讀到之前的值,這就會引發錯誤。
    爲了解決這個問題,必須保證增加操作是原子的,在JDK1.5之前我們可以使用同步技術來做到這一點。到JDK1.5,java.util.concurrent.atomic包提供了int和long類型的原子包裝類,它們可以自動的保證對於他們的操作是原子的並且不需要使用同步。
    java.util.concurrent這個包裏面提供了一組原子類。其基本的特性就是在多線程環境下,當有多個線程同時執行這些類的實例包含的方法時,具有排他性,即當某個線程進入方法,執行其中的指令時,不會被其他線程打斷,而別的線程就像自旋鎖一樣,一直等到該方法執行完成,才由JVM從等待隊列中選擇一個另一個線程進入,這只是一種邏輯上的理解。
    原子類:AtomicBoolean,AtomicInteger,AtomicLong,AtomicReference
    原子數組:AtomicIntegerArray,AtomicLongArray,AtomicReferenceArray
    原子屬性更新器:AtomicLongFieldUpdater,AtomicIntegerFieldUpdater,AtomicReferenceFieldUpdater
    解決ABA問題的原子類:AtomicMarkableReference(通過引入一個boolean來反映中間有沒有變過),AtomicStampedReference(通過引入一個int來累加來反映中間有沒有變過)

  • 回答:
  • 見分析。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章