面試專題 | Java多線程部分在面試過程中常會問到的問題整理

目錄

問題整理

1、什麼是線程和進程,他們的區別和聯繫?

2、線程創建的方式有幾種?分別是什麼?如何啓動一個線程?

3、線程的有幾種狀態?分別是什麼?

4、線程間幾種狀態的轉換?

5、線程的優先級

6、用戶線程和守護線程的區別

7、ReentrantLock可中斷鎖

8、鎖Synchronized和reentranLock的對比

9、怎麼讓線程按照順序執行

10、設計線程的標記位可以在任何時間break掉

11、死鎖產生條件

12、怎麼設計一個死鎖

13、LinkedBlockingQueue和ArrayBlockingQueue的區別

14、sleep() 和 wait() 有什麼區別

15、請說出你所知道的線程同步的方法。

16、什麼是線程餓死,什麼是活鎖?

17、volatile 變量是什麼?volatile 變量和 atomic 變量有什麼不同

18、volatile 類型變量提供什麼保證?能使得一個非原子操作變成原子操作嗎?

19、什麼是線程池

20、爲什麼使用線程池?線程池的作用

21、幾種常見的線程池及使用場景

22、線程池中的幾種重要的參數

23、線程池的工作流程

 


問題整理

1、什麼是線程和進程,他們的區別和聯繫?

進程是指運行的程序,比如計算機上運行起來的一個應用就是一個進程。進程是由線程組成的,一個進程可以包含多個線程,比如QQ聊天進程由一個發送線程+一個接收線程組成。而線程只是CPU的運行單位。

區別:

  1. 進程是資源分配的最小單位,而線程是程序執行的最小單位;
  2. 資源:進程是資源分配的單位,系統分配資源的時候給每個進程單獨資源,但線程是資源共享的,一個進程下的線程共享本進程的資源;
  3. 地址空間:每個進程都有一塊自己的獨立地址空間,而線程只是最爲一個入口,地址空間是共享的;
  4. 進程之間相互獨立,一個進程崩潰不會影響其他進程;但同一進程下的線程崩潰會影響本進程下的其他線程;

聯繫:

  1. 一個進程可以包含多個線程;
  2. 在沒有實現線程的操作系統中,進程是資源分配的基本單位,又是調度的基本單位,它是系統中併發執行的單元。在實現線程的操作系統中,進程是資源分配的基本單位,但線程是基本調度單元,是系統併發執行的單元。

2、線程創建的方式有幾種?分別是什麼?如何啓動一個線程?

答案參考上篇博客:https://blog.csdn.net/weixin_44187963/article/details/103459524

3、線程的有幾種狀態?分別是什麼?

線程有6種狀態,分別是:

new狀態:新建了一個線程對象,但還沒有啓動
runnable(就緒狀態):線程對象創建後調用start方法啓動線程,此時線程就處於runnable狀態
blocked(阻塞狀態):1)等待一個監視器鎖來進入同步代碼塊或同步方法
                  2)調用Object.wait()方法,此時需要notify/notifyAll去喚醒它
waiting(等待狀態):Object.wait()/notify/notifyAll,Thread.join(), LockSupport.park()
timed_waiting:Object.wait()/notify/notifyAll,Thread.join(), LockSupport.park()
terminated(終止狀態):線程執行完了之後就會進入到終止狀態

4、線程間幾種狀態的轉換?

5、線程的優先級

1)線程優先級的取值範圍:1到10

默認優先級=5 優先級由高到低10 9...2 1 優先級高的率先完成任務

2)設定線程的優先級:thread.setPriority(1)

3)優先級特性:

規則性:優先級高的率先完成任務的紀律更大;

隨機性:CPU會盡量將資源分配給優先級高的,所以我們看到的結果並不是一定優先級高的先執行完;

繼承性:在A線程下創建一個B線程,那麼B線程的優先級==A線程的優先級

6、用戶線程和守護線程的區別

結束時機:用戶線程結束時run方法執行完;而守護線程是等待所有用戶線程執行完畢之後;

守護線程使用場景:---GC:Java對象(垃圾)回收機制

7、ReentrantLock可中斷鎖

ReentrantLock實現的前提就是AbstractQueuedSynchronizer,簡稱AQS,是java.util.concurrent的核心。AQS是基於FIFO隊列的實現AQS中的隊列是由 Node節點組成的雙向鏈表實現的。所有的操作都是在這個AQS隊列當中。如果一個線程獲取鎖成功那就成功了,如果失敗了就將其放入等待隊列當中。

 

8、鎖Synchronized和reentranLock的對比

1)本質:synchronized是一個關鍵字,reentrantLock是一個類;

2)加鎖方式:synchronize只提供阻塞的加鎖方式,加鎖解鎖過程是由jvm控制;reentrantLock提供更加豐富的加鎖方式,加鎖解鎖需要手動調用相應的方法;

3)公平性:synchronized:非公平的鎖;reentrantLock:既可以實現公平的鎖、也可以實現非公平的鎖:實現方法爲改變改變構造函數中的參數,傳true實現爲公平,不傳或者傳false就實現爲非公平的;

4)synchronized:是非重入鎖; reentrantLock:是重入鎖;

5)實現原理(都是用了CAS操作):synchronized是mark word 和 monitor,reentrantLock藉助state 和AQS隊列;

6)和讀寫鎖的對比:

synchronized和讀寫鎖對比:讀讀不互斥,讀寫互斥,寫寫互斥;

reentrantLock和讀寫鎖對比:讀讀不互斥,讀寫互斥,寫寫互斥;

9、怎麼讓線程按照順序執行

優先級不可以,所以用join()插隊的方式實現線程的執行順序

10、設計線程的標記位可以在任何時間break掉

線程正常結束是:執行完run

設置斷點結束一個線程的時候,設置一個flag變量,一定得加volatile--強制進行同步,監聽工具 private volatile boolean flag = false;

11、死鎖產生條件

1)互斥:A線程和B線程,它們同時請求一部分資源,只有其中一個能拿到

2)請求保持:A線程和B線程,A和B都需要拿到1和2資源才能執行,所以AB分別拿了12那麼AB都處於請求保持

3)循環等待:假如AB分別拿了12,那麼它們每人佔用一個資源同時等待另一個資源釋放,所以都處於循環等待

4)不可分割:A在拿到資源之後如果沒有完成任務之前資源不能被別的線程搶佔

12、怎麼設計一個死鎖

syncharnized實現,需要有兩個鎖 lock1和lock2

1.首先需要有兩個或兩個以上的線程和資源

2.A和B都要保證把lock1和lock2都拿到才能執行後續代碼

3.A先拿到lock1且B先拿到lock2

13、LinkedBlockingQueue和ArrayBlockingQueue的區別

(1)方法:實例化對象時,ArrayBlockingQueue必須傳參數,LinkedBlockingQueue可不傳參數。

(2)底層:ArrayBlockingQueue底層是數組(適合隨機訪問,有容量),LinkedBlockingQueue底層是鏈表(適合插入刪除,可制定上限);

(3)鎖:ArrayBlockingQueue用的是單鎖,LinkedBlockingQueue用的是雙鎖;

(4)效率:ArrayBlockingQueue用的是單鎖效率較低,LinkedBlockingQueue用的是雙鎖效率較高;

14、sleep() 和 wait() 有什麼區別

sleep 就是正在執行的線程主動讓出 cpu,cpu 去執行其他線程,在 sleep 指定的時間過後,cpu 纔會回到這個線程上繼續往下執行,如果當前線程進入了同步鎖,sleep 方法並不會釋放鎖,即使當前線程使用 sleep 方法讓出了 cpu,但其他被同步鎖擋住了的線程也無法得到執行。wait 是指在一個已經進入了同步鎖的線程內,讓自己暫時讓出同步鎖,以便其他正在等待此鎖的線程可以得到同步鎖並運行,只有其他線程調用了 notify 方法(notify 並不釋放鎖,只是告訴調用過 wait 方法的線程可以去參與獲得鎖的競爭了,但不是馬上得到鎖,因爲鎖還在別人手裏,別人還沒釋放。如果 notify 方法後面的代碼還有很多,需要這些代碼執行完後纔會釋放鎖,可以在 notfiy 方法後增加一個等待和一些代碼,看看效果),調用 wait 方法的線程就會解除 wait 狀態和程序可以再次得到鎖後繼續向下運行。

15、請說出你所知道的線程同步的方法。

wait():使一個線程處於等待狀態,並且釋放所持有的對象的 lock。 sleep():使一個正在運行的線程處於睡眠狀態,是一個靜態方法,調用此方法要捕捉 InterruptedException異常。

notify():喚醒一個處於等待狀態的線程,注意的是在調用此方法的時候,並不能確切的喚醒某一個等待狀態的線程,而是由 JVM 確定喚醒哪個線程,而且不是按優先級。

notityAll():喚醒所有處入等待狀態的線程,注意並不是給所有喚醒線程一個對象的鎖,而是讓它們競爭。

16、什麼是線程餓死,什麼是活鎖?

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

• 當所有線程在序中執行 Object.wait(0),參數爲 0 的 wait 方法。程序將發生

活鎖直到在相應的對象上有線程調用 Object.notify() 或者 Object.notifyAll()。

• 當所有線程卡在無限循環中。

17、volatile 變量是什麼?volatile 變量和 atomic 變量有什麼不同

volatile 則是保證了所修飾的變量的可見。因爲 volatile 只是在保證了同一個變量在多線程中的可見性,所以它更多是用於修飾作爲開關狀態的變量,即 Boolean 類型的變量。

volatile 多用於修飾類似開關類型的變量、Atomic 多用於類似計數器相關的變量、其它多線程併發操作用 synchronized 關鍵字修飾。

volatile 有兩個功用:

• 這個變量不會在多個線程中存在複本,直接從內存讀取。

• 這個關鍵字會禁止指令重排序優化。也就是說,在 volatile 變量的賦值操作後面會有一個內存屏障(生成的彙編代碼上),讀操作不會被重排序到內存屏障之前。

18、volatile 類型變量提供什麼保證?能使得一個非原子操作變成原子操作嗎?

volatile 提供 happens-before 的保證,確保一個線程的修改能對其他線程是可見的。 在 Java 中除了 long 和 double 之外的所有基本類型的讀和賦值,都是原子性操作。而 64 位的 long 和 double 變量由於會被 JVM 當作兩個分離的 32 位來進行操作,所以不具有原子性,會產生字撕裂問題。但是當你定義 long 或 double 變量時,如果使用 volatile 關鍵字,就會獲到(簡單的賦值與返回操作的)原子性。

19、什麼是線程池

線程池是一種多線程處理形式,處理過程中將任務提交到線程池,任務的執行交由線程池來管理。如果每個請求都創建一個線程去處理,那麼服務器的資源很快就會被耗盡,使用線程池可以減少創建和銷燬線程的次數,每個工作線程都可以被重複利用,可執行多個任務。

20、爲什麼使用線程池?線程池的作用

創建線程和銷燬線程的花銷是比較大的,這些時間有可能比處理業務的時間還要長。這樣頻繁的創建線程和銷燬線程,再加上業務工作線程,消耗系統資源的時間,可能導致系統資源不足。

線程池作用就是限制系統中執行線程的數量,1、提高效率 創建好一定數量的線程放在池中,等需要使用的時候就從池中拿一個,這要比需要的時候創建一個線程對象要快的多。2、方便管理 可以編寫線程池管理代碼對池中的線程同一進行管理,比如說啓動時有該程序創建100個線程,每當有請求的時候,就分配一個線程去工作,如果剛好併發有101個請求,那多出的這一個請求可以排隊等候,避免因無休止的創建線程導致系統崩潰。

21、幾種常見的線程池及使用場景

1、newSingleThreadExecutor

創建一個單線程化的線程池,它只會用唯一的工作線程來執行任務,保證所有任務按照指定順序(FIFO, LIFO, 優先級)執行。

2、newFixedThreadPool

創建一個定長線程池,可控制線程最大併發數,超出的線程會在隊列中等待。

3、newCachedThreadPool

創建一個可緩存線程池,如果線程池長度超過處理需要,可靈活回收空閒線程,若無可回收,則新建線程。

4、newScheduledThreadPool

創建一個定長線程池,支持定時及週期性任務執行。

22、線程池中的幾種重要的參數

corePoolSize:就是線程池中的核心線程數量,這幾個核心線程,只是在沒有用的時候,也不會被回收

maximumPoolSize:就是線程池中可以容納的最大線程的數量

keepAliveTime:就是線程池中除了核心線程之外的其他的最長可以保留的時間,因爲在線程池中,除了核心線程即使在無任務的情況下也不能被清除,其餘的都是有存活時間的,意思就是非核心線程可以保留的最長的空閒時間,

util:就是計算這個時間的一個單位。

workQueu:就是等待隊列,任務可以儲存在任務隊列中等待被執行,執行的是FIFIO原則(先進先出)。

threadFactory:就是創建線程的線程工廠。

handle:是一種拒絕策略,我們可以在任務滿了之後,拒絕執行某些任務。

23、線程池的工作流程

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

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