多線程面試題(2020)

多線程算法

1.並行和併發有什麼區別?數據庫

並行:多個任務在同一個 CPU 核上,按細分的時間片輪流(交替)執行,從邏輯緩存

上來看那些任務是同時執行。安全

併發:多個處理器或多核處理器同時處理多個任務。session

以下圖:多線程

併發和並行併發

併發 = 兩個隊列和一臺咖啡機。jvm

並行 = 兩個隊列和兩臺咖啡機。性能

2.線程和進程的區別?優化

一個程序下至少有一個進程,一個進程下至少有一個線程,一個進程下也能夠有

多個線程來增長程序的執行速度。

3.守護線程是什麼?

守護線程是運行在後臺的一種特殊進程。它獨立於控制終端而且週期性地執行某

種任務或等待處理某些發生的事件。在 Java 中垃圾回收線程就是特殊的守護線

程。

4.建立線程有哪幾種方式?

建立線程有三種方式:

繼承 Thread 從新 run 方法;

實現 Runnable 接口;

實現 Callable 接口。

5. 說一下 runnable 和 callable 有什麼區別?

runnable 沒有返回值,

callable 能夠拿到有返回值,

callable 能夠看做是 runnable

的補充。

6.線程有哪些狀態?

線程的狀態:

NEW 還沒有啓動RUNNABLE 正在執行中

BLOCKED 阻塞的(被同步鎖或者 IO 鎖阻塞)

WAITING 永久等待狀態

TIMED_WAITING 等待指定的時間從新被喚醒的狀態

TERMINATED 執行完成

7. sleep() 和 wait() 有什麼區別?

類的不一樣:sleep() 來自 Thread,wait() 來自 Object。

釋放鎖:sleep() 不釋放鎖;wait() 釋放鎖。

用法不一樣:sleep() 時間到會自動恢復;wait() 可使用 notify()/notifyAll()直接喚

醒。

8. notify()和 notifyAll()有什麼區別?

notifyAll()會喚醒全部的線程,notify()以後喚醒一個線程。notifyAll() 調用後,會

將所有線程由等待池移到鎖池,而後參與鎖的競爭,競爭成功則繼續執行,若是

不成功則留在鎖池等待鎖被釋放後再次參與競爭。而 notify()只會喚醒一個線程,

具體喚醒哪個線程由虛擬機控制。

9.線程的 run() 和 start() 有什麼區別?

start() 方法用於啓動線程,run() 方法用於執行線程的運行時代碼。run() 能夠重

復調用,而 start() 只能調用一次。

10.建立線程池有哪幾種方式?

線程池建立有七種方式,最核心的是最後一種:

newSingleThreadExecutor():它的特色在於工做線程數目被限制爲 1,操做一個

無界的工做隊列,因此它保證了全部任務的都是被順序執行,最多會有一個任務

處於活動狀態,而且不容許使用者改動線程池實例,所以能夠避免其改變線程數

目;

newCachedThreadPool():它是一種用來處理大量短期工做任務的線程池,具備

幾個鮮明特色:它會試圖緩存線程並重用,當無緩存線程可用時,就會建立新的

工做線程;若是線程閒置的時間超過 60 秒,則被終止並移出緩存;長時間閒置

時,這種線程池,不會消耗什麼資源。其內部使用 SynchronousQueue 做爲工做

隊列;

newFixedThreadPool(int nThreads):重用指定數目(nThreads)的線程,其背後使

用的是無界的工做隊列,任什麼時候候最多有 nThreads 個工做線程是活動的。這意

味着,若是任務數量超過了活動隊列數目,將在工做隊列中等待空閒線程出現;

若是有工做線程退出,將會有新的工做線程被建立,以補足指定的數目

nThreads;newSingleThreadScheduledExecutor() : 創 建 單 線 程 池 , 返 回

ScheduledExecutorService,能夠進行定時或週期性的工做調度;

newScheduledThreadPool(int corePoolSize):和 newSingleThreadScheduledExecutor()

相似,建立的是個 ScheduledExecutorService,能夠進行定時或週期性的工做調

度,區別在於單一工做線程仍是多個工做線程;

newWorkStealingPool(int parallelism):這是一個常常被人忽略的線程池,Java 8 才

加入這個建立方法,其內部會構建 ForkJoinPool,利用 Work-Stealing 算法,並行

地處理任務,不保證處理順序;

ThreadPoolExecutor():是最原始的線程池建立,上面 1-3 建立方式都是對

ThreadPoolExecutor 的封裝。

11.線程池都有哪些狀態?

RUNNING:這是最正常的狀態,接受新的任務,處理等待隊列中的任務。

SHUTDOWN:不接受新的任務提交,可是會繼續處理等待隊列中的任務。

STOP:不接受新的任務提交,再也不處理等待隊列中的任務,中斷正在執行任務的

線程。

TIDYING:全部的任務都銷燬了,workCount 爲 0,線程池的狀態在轉換爲

TIDYING 狀態時,會執行鉤子方法 terminated()。

TERMINATED:terminated()方法結束後,線程池的狀態就會變成這個。

線程池中 submit() 和 execute() 方法有什麼區別?

execute():只能執行 Runnable 類型的任務。

submit():能夠執行 Runnable 和 Callable 類型的任務。

Callable 類型的任務能夠獲取執行的返回值,而 Runnable 執行無返回值。

12.在 Java 程序中怎麼保證多線程的運行安全?

方法一:使用安全類,好比 Java. util. concurrent 下的類。

方法二:使用自動鎖 synchronized。

方法三:使用手動鎖 Lock。

手動鎖 Java 示例代碼以下:

Lock lock = new ReentrantLock();

lock. lock();

try {

System. out. println(「得到鎖」);

} catch (Exception e) {

// TODO: handle exception

} finally {

System. out. println(「釋放鎖」);

lock. unlock();}

13. 多線程中 synchronized 鎖升級的原理是什麼?

synchronized 鎖升級原理:在鎖對象的對象頭裏面有一個 threadid 字段,在第

一次訪問的時候 threadid 爲空,jvm 讓其持有偏向鎖,並將 threadid 設置爲其

線程 id,再次進入的時候會先判斷 threadid 是否與其線程 id 一致,若是一致

則能夠直接使用此對象,若是不一致,則升級偏向鎖爲輕量級鎖,經過自旋循環

必定次數來獲取鎖,執行必定次數以後,若是尚未正常獲取到要使用的對象,

此時就會把鎖從輕量級升級爲重量級鎖,此過程就構成了 synchronized 鎖的升

級。

鎖的升級的目的:鎖升級是爲了減低了鎖帶來的性能消耗。在 Java 6 以後優化

synchronized 的實現方式,使用了偏向鎖升級爲輕量級鎖再升級到重量級鎖的方

式,從而減低了鎖帶來的性能消耗。

14.什麼是死鎖?

當線程 A 持有獨佔鎖 a,並嘗試去獲取獨佔鎖 b 的同時,線程 B 持有獨佔鎖

b,並嘗試獲取獨佔鎖 a 的狀況下,就會發生 AB 兩個線程因爲互相持有對方

須要的鎖,而發生的阻塞現象,咱們稱爲死鎖。

15.怎麼防止死鎖?

盡 量 使 用

tryLock(long timeout, TimeUnit unit) 的 方 法 (ReentrantLock 、

ReentrantReadWriteLock),設置超時時間,超時能夠退出防止死鎖。

儘可能使用 Java. util. concurrent 併發類代替本身手寫鎖。

儘可能下降鎖的使用粒度,儘可能不要幾個功能用同一把鎖。

儘可能減小同步的代碼塊。

16.ThreadLocal 是什麼?有哪些使用場景?

ThreadLocal 爲每一個使用該變量的線程提供獨立的變量副本,因此每個線程都

能夠獨立地改變本身的副本,而不會影響其它線程所對應的副本。

ThreadLocal 的經典使用場景是數據庫鏈接和 session 管理等。

17.說一下 synchronized 底層實現原理?

synchronized 是由一對 monitorenter/monitorexit 指令實現的,monitor 對象是

同步的基本實現單元。在 Java 6 以前,monitor 的實現徹底是依靠操做系統內

部的互斥鎖,由於須要進行用戶態到內核態的切換,因此同步操做是一個無差異

的重量級操做,性能也很低。但在 Java 6 的時候,Java 虛擬機 對此進行了大刀闊斧地改進,提供了三種不一樣的 monitor 實現,也就是常說的三種不一樣的鎖:

偏向鎖(Biased Locking)、輕量級鎖和重量級鎖,大大改進了其性能。

18.synchronized 和 volatile 的區別是什麼?

volatile 是變量修飾符;synchronized 是修飾類、方法、代碼段。

volatile 僅能實現變量的修改可見性,不能保證原子性;而 synchronized 則能夠

保證變量的修改可見性和原子性。

volatile 不會形成線程的阻塞;synchronized 可能會形成線程的阻塞。

19.synchronized 和 Lock 有什麼區別?

synchronized 能夠給類、方法、代碼塊加鎖;而 lock 只能給代碼塊加鎖。

synchronized 不須要手動獲取鎖和釋放鎖,使用簡單,發生異常會自動釋放鎖,

不會形成死鎖;而 lock 須要本身加鎖和釋放鎖,若是使用不當沒有 unLock()去

釋放鎖就會形成死鎖。

經過 Lock 能夠知道有沒有成功獲取鎖,而 synchronized 卻沒法辦到。

20.synchronized 和 ReentrantLock 區別是什麼?

synchronized 早期的實現比較低效,對比 ReentrantLock,大多數場景性能都相

差較大,可是在 Java 6 中對 synchronized 進行了很是多的改進。

主要區別以下:

ReentrantLock 使用起來比較靈活,可是必須有釋放鎖的配合動做;

ReentrantLock 必須手動獲取與釋放鎖,而 synchronized 不須要手動釋放和開啓

鎖;

ReentrantLock 只適用於代碼塊鎖,而 synchronized 可用於修飾方法、代碼塊等。

volatile 標記的變量不會被編譯器優化;synchronized 標記的變量能夠被編譯器

優化。

21. 說一下 atomic 的原理?

atomic 主要利用 CAS (Compare And Wwap) 和 volatile 和 native 方法來保證原

子操做,從而避免 synchronized 的高開銷,執行效率大爲提高。

相關文章
相關標籤/搜索