高頻多線程&併發面試題(附答案,純乾貨)(一)

JAVA 併發知識庫

在這裏插入圖片描述

一、Java中實現多線程有幾種方法

繼承Thread類;
實現Runnable接口;
實現Callable接口經過FutureTask包裝器來建立Thread線程;
使用ExecutorService、Callable、Future實現有返回結果的多線程(也就是使用了ExecutorService來管理前面的三種方式)。 java

二、繼承 Thread 類

Thread 類本質上是實現了 Runnable 接口的一個實例,表明一個線程的實例。 啓動線程的惟一方法就是經過 Thread 類的 start()實例方法。 start()方法是一個 native 方法,它將啓動一個新線程,並執行 run()方法。 在這裏插入圖片描述面試

三、實現 Runnable 接口。

若是本身的類已經 extends 另外一個類,就沒法直接 extends Thread,此時,能夠實現一個Runnable 接口。 在這裏插入圖片描述數據庫

四、ExecutorService、 Callable、 Future 有返回值線程

有返回值的任務必須實現 Callable 接口,相似的,無返回值的任務必須 Runnable 接口。執行Callable 任務後,能夠獲取一個 Future 的對象,在該對象上調用 get 就能夠獲取到 Callable 任務返回的 Object 了,再結合線程池接口 ExecutorService 就能夠實現傳說中有返回結果的多線程了。 在這裏插入圖片描述緩存

五、基於線程池的方式

線程和數據庫鏈接這些資源都是很是寶貴的資源。那麼每次須要的時候建立,不須要的時候銷燬,是很是浪費資源的。那麼咱們就可使用緩存的策略,也就是使用線程池。 在這裏插入圖片描述安全

六、4 種線程池

Java 裏面線程池的頂級接口是 Executor,可是嚴格意義上講 Executor 並非一個線程池,而只是一個執行線程的工具。真正的線程池接口是 ExecutorService。 在這裏插入圖片描述多線程

newCachedThreadPool
建立一個可根據須要建立新線程的線程池,可是在之前構造的線程可用時將重用它們。對於執行不少短時間異步任務的程序而言,這些線程池一般可提升程序性能。 調用 execute 將重用之前構造的線程(若是線程可用)。若是現有線程沒有可用的,則建立一個新線程並添加到池中。終止並從緩存中移除那些已有 60 秒鐘未被使用的線程。 所以,長時間保持空閒的線程池不會使用任何資源。
newFixedThreadPool
建立一個可重用固定線程數的線程池,以共享的***隊列方式來運行這些線程。在任意點,在大多數 nThreads 線程會處於處理任務的活動狀態。若是在全部線程處於活動狀態時提交附加任務,則在有可用線程以前,附加任務將在隊列中等待。若是在關閉前的執行期間因爲失敗而致使任何線程終止,那麼一個新線程將代替它執行後續的任務(若是須要)。在某個線程被顯式地關閉以前,池中的線程將一直存在。
newScheduledThreadPool
建立一個線程池,它可安排在給定延遲後運行命令或者按期地執行。 在這裏插入圖片描述
newSingleThreadExecutor
Executors.newSingleThreadExecutor()返回一個線程池(這個線程池只有一個線程) ,這個線程池能夠在線程死後(或發生異常時)從新啓動一個線程來替代原來的線程繼續執行下去! 併發

七、如何中止一個正在運行的線程

1.使用退出標誌,使線程正常退出,也就是當run方法完成後線程終止。
2.使用stop方法強行終止,可是不推薦這個方法,由於stop和suspend及resume同樣都是過時做廢的方法。
3.使用interrupt方法中斷線程。在這裏插入圖片描述異步

八、notify()和notifyAll()有什麼區別?

notify可能會致使死鎖,而notifyAll則不會
任什麼時候候只有一個線程能夠得到鎖,也就是說只有一個線程能夠運行synchronized 中的代碼使用notifyall,能夠喚醒全部處於wait狀態的線程,使其從新進入鎖的爭奪隊列中,而notify只能喚醒一個。
wait() 應配合while循環使用,不該使用if,務必在wait()調用先後都檢查條件,若是不知足,必須調用notify()喚醒另外的線程來處理,本身繼續wait()直至條件知足再往下執行。
notify() 是對notifyAll()的一個優化,但它有很精確的應用場景,而且要求正確使用。否則可能致使死鎖。正確的場景應該是 WaitSet中等待的是相同的條件,喚醒任一個都能正確處理接下來的事項,若是喚醒的線程沒法正確處理,務必確保繼續notify()下一個線程,而且自身須要從新回到WaitSet中. jvm

九、sleep()和wait() 有什麼區別?

  1. 對於 sleep()方法,咱們首先要知道該方法是屬於 Thread 類中的。而 wait()方法,則是屬於Object 類中的。
  2. sleep()方法致使了程序暫停執行指定的時間,讓出 cpu 該其餘線程,可是他的監控狀態依然保持者,當指定的時間到了又會自動恢復運行狀態
  3. 在調用 sleep()方法的過程當中, 線程不會釋放對象鎖。
  4. 而當調用 wait()方法的時候,線程會放棄對象鎖,進入等待此對象的等待鎖定池,只有針對此對象調用 notify()方法後本線程才進入對象鎖定池準備獲取對象鎖進入運行狀態。

十、volatile 是什麼?能夠保證有序性嗎?

一旦一個共享變量(類的成員變量、類的靜態成員變量)被volatile修飾以後,那麼就具有了兩層語義:
1)保證了不一樣線程對這個變量進行操做時的可見性,即一個線程修改了某個變量的值,這新值對其餘線程來講是當即可見的,volatile關鍵字會強制將修改的值當即寫入主存。
2)禁止進行指令重排序。
volatile 不是原子性操做
什麼叫保證部分有序性?
當程序執行到volatile變量的讀操做或者寫操做時,在其前面的操做的更改確定所有已經進行,且結果已經對後面的操做可見;在其後面的操做確定尚未進行;
在這裏插入圖片描述
因爲flag變量爲volatile變量,那麼在進行指令重排序的過程的時候,不會將語句3放到語句一、語句2前面,也不會講語句3放到語句四、語句5後面。可是要注意語句1和語句2的順序、語句4和語句5的順序是不做任何保證的。使用 Volatile 通常用於 狀態標記量 和 單例模式的雙檢鎖 ide

十一、Thread 類中的start() 和 run() 方法有什麼區別?

start()方法被用來啓動新建立的線程,並且start()內部調用了run()方法,這和直接調用run()方法的效果不同。當你調用run()方法的時候,只會是在原來的線程中調用,沒有新的線程啓動,start()方法纔會啓動新線程 。

十二、爲何wait, notify 和 notifyAll這些方法不在thread類裏面?

明顯的緣由是JAVA提供的鎖是對象級的而不是線程級的,每一個對象都有鎖,經過線程得到。若是線程須要等待某些鎖那麼調用對象中的wait()方法就有意義了。若是wait()方法定義在Thread類中,線程正在等待的是哪一個鎖就不明顯了。簡單的說,因爲wait,notify和notifyAll都是鎖級別的操做,因此把他們定義在Object類中由於鎖屬於對象 。

1三、爲何wait和notify方法要在同步塊中調用?

  1. 只有在調用線程擁有某個對象的獨佔鎖時,纔可以調用該對象的wait(),notify()和notifyAll()方法。
  2. 若是你不這麼作,你的代碼會拋出IllegalMonitorStateException異常。
  3. 還有一個緣由是爲了不wait和notify之間產生競態條件。
    wait()方法強制當前線程釋放對象鎖。這意味着在調用某對象的wait()方法以前,當前線程必須已經得到該對象的鎖。所以,線程必須在某個對象的同步方法或同步代碼塊中才能調用該對象的wait()方法。
    在調用對象的notify()和notifyAll()方法以前,調用線程必須已經獲得該對象的鎖。所以,必須在某個對象的同步方法或同步代碼塊中才能調用該對象的notify()或notifyAll()法。
    調用wait()方法的緣由一般是,調用線程但願某個特殊的狀態(或變量)被設置以後再繼續執行。調用notify()或notifyAll()方法的緣由一般是,調用線程但願告訴其餘等待中的線程:"特殊狀態已經被設置"。
    這個狀態做爲線程間通訊的通道,它必須是一個可變的共享狀態(或變量)。

1四、Java中interrupted 和 isInterruptedd方法的區別?

interrupted() 和 isInterrupted()的主要區別是前者會將中斷狀態清除然後者不會。Java多線程的中斷機制是用內部標識來實現的,調用Thread.interrupt()來中斷一個線程就會設置中斷標識爲true。
當中斷線程調用靜態方法Thread.interrupted()來檢查中斷狀態時,中斷狀態會被清零。
而非靜態方法isInterrupted()用來查詢其它線程的中斷狀態且不會改變中斷狀態標識。簡單的說就是任何拋出InterruptedException異常的方法都會將中斷狀態清零。不管如何,一個線程的中斷狀態有有可能被其它線程調用中斷來改變 。

1五、Java中synchronized 和 ReentrantLock 有什麼不一樣?

類似點:
這兩種同步方式有不少類似之處,它們都是加鎖方式同步,並且都是阻塞式的同步,也就是說當若是一個線程得到了對象鎖,進入了同步塊,其餘訪問該同步塊的線程都必須阻塞在同步塊外面等待,而進行線程阻塞和喚醒的代價是比較高的.
區別:
這兩種方式最大區別就是對於Synchronized來講,它是java語言的關鍵字,是原生語法層面的互斥,須要jvm實現。而ReentrantLock它是JDK 1.5以後提供的API層面的互斥鎖,須要lock()和unlock()方法配合try/finally語句塊來完成。
Synchronized進過編譯,會在同步塊的先後分別造成monitorenter和monitorexit這個兩個字節碼指令。在執行monitorenter指令時,首先要嘗試獲取對象鎖。若是這個對象沒被鎖定,或者當前線程已經擁有了那個對象鎖,把鎖的計算器加1,相應的,在執行monitorexit指令時會將鎖計算器就減1,當計算器爲0時,鎖就被釋放了。若是獲取對象鎖失敗,那當前線程就要阻塞,直到對象鎖被另外一個線程釋放爲止 。
因爲ReentrantLock是java.util.concurrent包下提供的一套互斥鎖,相比Synchronized,ReentrantLock類提供了一些高級功能,主要有如下3項:
1.等待可中斷,持有鎖的線程長期不釋放的時候,正在等待的線程能夠選擇放棄等待,這至關於Synchronized來講能夠避免出現死鎖的狀況。
2.公平鎖,多個線程等待同一個鎖時,必須按照申請鎖的時間順序得到鎖,Synchronized鎖非公平鎖,ReentrantLock默認的構造函數是建立的非公平鎖,能夠經過參數true設爲公平鎖,但公平鎖表現的性能不是很好。
3.鎖綁定多個條件,一個ReentrantLock對象能夠同時綁定對個對象 。

1六、有三個線程T1,T2,T3,如何保證順序執行?

在多線程中有多種方法讓線程按特定順序執行,你能夠用線程類的join()方法在一個線程中啓動另外一個線程,另一個線程完成該線程繼續執行。爲了確保三個線程的順序你應該先啓動最後一個(T3調用T2,T2調用T1),這樣T1就會先完成而T3最後完成。
實際上先啓動三個線程中哪個都行,由於在每一個線程的run方法中用join方法限定了三個線程的執行順序 。
在這裏插入圖片描述

1七、SynchronizedMap和ConcurrentHashMap有什麼區別?

SynchronizedMap()和Hashtable同樣,實現上在調用map全部方法時,都對整個map進行同步。而ConcurrentHashMap的實現卻更加精細,它對map中的全部桶加了鎖。因此,只要有一個線程訪問map,其餘線程就沒法進入map,而若是一個線程在訪問ConcurrentHashMap某個桶時,其餘線程,仍然能夠對map執行某些操做。因此,ConcurrentHashMap在性能以及安全性方面,明顯比Collections.synchronizedMap()更加有優點。同時,同步操做精確控制到桶,這樣,即便在遍歷map時,若是其餘線程試圖對map進行數據修改,也不會拋出ConcurrentModificationException 。

1八、什麼是線程安全

線程安全就是說多線程訪問同一代碼,不會產生不肯定的結果。
在多線程環境中,當各線程不共享數據的時候,即都是私有(private)成員,那麼必定是線程安全的。
但這種狀況並很少見,在多數狀況下須要共享數據,這時就須要進行適當的同步控制了。
線程安全通常都涉及到synchronized, 就是一段代碼同時只能有一個線程來操做 否則中間過程可能會產生不可預製的結果。
若是你的代碼所在的進程中有多個線程在同時運行,而這些線程可能會同時運行這段代碼。若是每次運行結果和單線程運行的結果是同樣的,並且其餘的變量的值也和預期的是同樣的,就是線程安全的。

1九、Thread類中的yield方法有什麼做用?

Yield方法能夠暫停當前正在執行的線程對象,讓其它有相同優先級的線程執行。它是一個靜態方法並且只保證當前線程放棄CPU佔用而不能保證使其它線程必定能佔用CPU,執行yield()的線程有可能在進入到暫停狀態後立刻又被執行。

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

兩個方法均可以向線程池提交任務,execute()方法的返回類型是void,它定義在Executor接口中, 而submit()方法能夠返回持有計算結果的Future對象,它定義在ExecutorService接口中,它擴展了Executor接口,其它線程池類像ThreadPoolExecutor和
ScheduledThreadPoolExecutor都有這些方法 。

更多面試題,歡迎關注公衆號【慕容千語】

相關文章
相關標籤/搜索