【2021最新版】Java多線程&併發面試題總結(108道題含答案解析)

文章目錄

最近面試的小夥伴不少,對此我整理了一份Java面試題手冊:基礎知識、JavaOOP、Java集合/泛型面試題、Java異常面試題、Java中的IO與NIO面試題、Java反射、Java序列化、Java註解、多線程&併發、JVM、Mysql、Redis、Memcached、MongoDB、Spring、SpringBoot、SpringCloud、RabbitMQ、Dubbo、MyBatis、ZooKeeper、數據結構、算法、Elasticsearch、Kafka、微服務、Linux等等。能夠分享給你們學習。【持續更新中】java

完整版Java面試題地址:【2021最新版】Java面試真題彙總web

序號 內容 地址連接
1 【2021最新版】JavaOOP面試題總結 http://www.noobyard.com/article/p-rncfmibs-oe.html
2 【2021最新版】Java基礎面試題總結 http://www.noobyard.com/article/p-ykqnztan-oe.html
3 【2021最新版】JVM面試題總結 未更新
4 【2021最新版】Mysql面試題總結 未更新
5 【2021最新版】Redis面試題總結 未更新
6 【2021最新版】Memcached面試題總結 未更新
7 【2021最新版】MongoDB面試題總結 未更新
8 【2021最新版】Spring面試題總結 未更新
9 【2021最新版】Spring Boot面試題總結 未更新
10 【2021最新版】Spring Cloud面試題總結 未更新
11 【2021最新版】RabbitMQ面試題總結 未更新
12 【2021最新版】Dubbo面試題總結 未更新
13 【2021最新版】MyBatis面試題總結 未更新
14 【2021最新版】ZooKeeper面試題總結 未更新
15 【2021最新版】數據結構面試題總結 未更新
16 【2021最新版】算法面試題總結 未更新
17 【2021最新版】Elasticsearch面試題總結 未更新
18 【2021最新版】Kafka面試題總結 未更新
19 【2021最新版】微服務面試題總結 未更新
20 【2021最新版】Linux面試題總結 未更新

JAVA併發知識庫

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

二、繼承Thread類

答:
Thread 類本質上是實現了Runnable接口的一個實例,表明一個線程的實例。 啓動線程的惟一方法就是經過Thread類的start()實例方法。 start()方法是一個native方法,它將啓動一個新線程,並執行run()方法。面試

public class MyThread extends Thread { 
 
  
	public void run() { 
 
  
		System.out.println("MyThread.run()");
	}
}
MyThread myThread1 = new MyThread();
myThread1.start();

三、實現Runnable接口。

答:算法

若是本身的類已經extends另外一個類,就沒法直接extends Thread,此時,能夠實現一個Runnable接口。sql

public class MyThread extends OtherClass implements Runnable { 
 
  
	public void run() { 
 
  
		System.out.println("MyThread.run()");
	}
}
//啓動 MyThread,須要首先實例化一個 Thread,並傳入本身的 MyThread 實例: MyThread myThread = new MyThread(); Thread thread = new Thread(myThread); thread.start(); //事實上,當傳入一個 Runnable target 參數給 Thread 後, Thread 的 run()方法就會調用 target.run() public void run() { if (target != null) { target.run(); } }

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

答:
有返回值的任務必須實現Callable接口,相似的,無返回值的任務必須Runnable接口。執行Callable任務後,能夠獲取一個 Future的對象,在該對象上調用get就能夠獲取到Callable任務返回的Object了,再結合線程池接口ExecutorService就能夠實現傳說中有返回結果的多線程了。數據庫

//建立一個線程池 ExecutorService pool = Executors.newFixedThreadPool(taskSize); // 建立多個有返回值的任務 List<Future> list = new ArrayList<Future>(); for (int i = 0; i < taskSize; i++) { Callable c = new MyCallable(i + " "); // 執行任務並獲取 Future 對象 Future f = pool.submit(c); list.add(f); }// 關閉線程池 pool.shutdown(); // 獲取全部併發任務的運行結果 for (Future f : list) { // 從 Future 對象上獲取任務的返回值,並輸出到控制檯 System.out.println("res: " + f.get().toString()); }

五、基於線程池的方式

六、4 種線程池

答:
Java 裏面線程池的頂級接口是Executor,可是嚴格意義上講Executor並非一個線程池,而只是一個執行線程的工具。真正的線程池接口是ExecutorService。

newCachedThreadPool數組

建立一個可根據須要建立新線程的線程池,可是在之前構造的線程可用時將重用它們。對於執行不少短時間異步任務的程序而言,這些線程池一般可提升程序性能。 調用execute將重用之前構造的線程(若是線程可用)。若是現有線程沒有可用的,則建立一個新線程並添加到池中。終止並從緩存中移除那些已有60秒鐘未被使用的線程。 所以,長時間保持空閒的線程池不會使用任何資源。緩存

newFixedThreadPool安全

建立一個可重用固定線程數的線程池,以共享的無界隊列方式來運行這些線程。在任意點,在大多數nThreads線程會處於處理任務的活動狀態。若是在全部線程處於活動狀態時提交附加任務,則在有可用線程以前,附加任務將在隊列中等待。若是在關閉前的執行期間因爲失敗而致使任何線程終止,那麼一個新線程將代替它執行後續的任務(若是須要)。在某個線程被顯式地關閉以前,池中的線程將一直存在。數據結構

newScheduledThreadPool

建立一個線程池,它可安排在給定延遲後運行命令或者按期地執行。

ScheduledExecutorService scheduledThreadPool= Executors.newScheduledThreadPool(3);
scheduledThreadPool.schedule(newRunnable(){ 
 
  
	@Override public void run() { 
 
  
		System.out.println("延遲三秒");
	}
}
, 3, TimeUnit.SECONDS);
scheduledThreadPool.scheduleAtFixedRate(newRunnable(){ 
 
  
	@Override public void run() { 
 
  
		System.out.println("延遲 1 秒後每三秒執行一次");
	}
}
,1,3,TimeUnit.SECONDS);

newSingleThreadExecutor

Executors.newSingleThreadExecutor()返回一個線程池(這個線程池只有一個線程),這個線程池能夠在線程死後(或發生異常時)從新啓動一個線程來替代原來的線程繼續執行下去!

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

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

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

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

答:

一旦一個共享變量(類的成員變量、類的靜態成員變量)被volatile修飾以後,那麼就具有了兩層語義:

1)保證了不一樣線程對這個變量進行操做時的可見性,即一個線程修改了某個變量的值,這新值對其餘線程來講是當即可見的,volatile關鍵字會強制將修改的值當即寫入主存。

2)禁止進行指令重排序。

volatile 不是原子性操做

什麼叫保證部分有序性?

當程序執行到volatile變量的讀操做或者寫操做時,在其前面的操做的更改確定所有已經進行,且結果已經對後面的操做可見;在其後面的操做確定尚未進行;

x = 2;
//語句1 y = 0; //語句2 flag = true; //語句3 x = 4; //語句4 y = -1; //語句5

因爲flag變量爲volatile變量,那麼在進行指令重排序的過程的時候,不會將語句3放到語句一、語句2前面,也不會講語句3放到語句四、語句5後面。
可是要注意語句1和語句2的順序、語句4和語句5的順序是不做任何保證的。使用 Volatile 通常用於狀態標記量和單例模式的雙檢鎖。

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

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

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

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

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

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

答:

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

public class JoinTest2 { 
 
  
	// 1.如今有T一、T二、T3三個線程,你怎樣保證T2在T1執行完後執行,T3在T2執行完後執行
	public static void main(String[] args) { 
 
  
		final Thread t1 = new Thread(new Runnable() { 
 
  
			@Override public void run() { 
 
  
				System.out.println("t1");
			}
		}
		);
		final Thread t2 = new Thread(new Runnable() { 
 
  
			@Override public void run() { 
 
  
				try { 
 
  
					// 引用t1線程,等待t1線程執行完 t1.join(); } catch (InterruptedException e) { e.printStackTrace(); } S ystem.out.println("t2"); } }); Thread t3 = new Thread(new Runnable() { @Override public void run() { try { // 引用t2線程,等待t2線程執行完 t2.join(); } catch (InterruptedException e) { e.printStackTrace(); } S ystem.out.println("t3"); } }); t3.start();//這裏三個線程的啓動順序能夠任意,你們能夠試下! t2.start(); t1.start(); } }

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

1八、什麼是線程安全?

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

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

2一、說一說本身對於synchronized關鍵字的瞭解。

2二、說說本身是怎麼使用synchronized關鍵字,在項目中用到了嗎?synchronized關鍵字最主要的三種使用方式

2三、什麼是線程安全?Vector是一個線程安全類嗎?

2四、volatile關鍵字的做用?

2五、簡述一下你對線程池的理解。

答:

若是問到了這樣的問題,能夠展開的說一下線程池如何用、線程池的好處、線程池的啓動策略)合理利用線程池可以帶來三個好處。

第一:下降資源消耗。經過重複利用已建立的線程下降線程建立和銷燬形成的消耗。

第二:提升響應速度。當任務到達時,任務能夠不須要等到線程建立就能當即執行。

第三:提升線程的可管理性。線程是稀缺資源,若是無限制的建立,不只會消耗系統資源,還會下降系統的穩定性,使用線程池能夠進行統一的分配,調優和監控。

2六、線程生命週期(狀態)

2七、新建狀態(NEW)

2八、就緒狀態(RUNNABLE)

2九、運行狀態(RUNNING)

30、阻塞狀態(BLOCKED)

3一、線程死亡(DEAD)

3二、終止線程4種方式

3三、start與run區別

3四、JAVA後臺線程

3五、什麼是樂觀鎖?

3六、什麼是悲觀鎖?

3七、什麼是自旋鎖?

3八、Synchronized同步鎖

3九、ReentrantLock

40、Condition類和Object類鎖方法區別區別?

4一、tryLock和lock和lockInterruptibly的區別?

4二、Semaphore信號量

答:

Semaphore 是一種基於計數的信號量。它能夠設定一個閾值,基於此,多個線程競爭獲取許可信號,作完本身的申請後歸還,超過閾值後,線程申請許可信號將會被阻塞。 Semaphore 能夠用來構建一些對象池,資源池之類的, 好比數據庫鏈接池
實現互斥鎖(計數器爲 1)咱們也能夠建立計數爲 1 的 Semaphore,將其做爲一種相似互斥鎖的機制,這也叫二元信號量,
表示兩種互斥狀態。

代碼實現

// 建立一個計數閾值
// 只能 5 個線程同時訪問 Semaphore semp = new Semaphore(5); try { // 申請許可 semp.acquire(); try { // 業務邏輯 } catch (Exception e) { } finally { // 釋放許可semp.release(); } } catch (InterruptedException e) { }

4三、Semaphore與ReentrantLock區別?

4四、可重入鎖(遞歸鎖)

4五、公平鎖與非公平鎖

4六、ReadWriteLock讀寫鎖

4七、共享鎖和獨佔鎖

4八、重量級鎖(Mutex Lock)

4九、輕量級鎖

50、偏向鎖

答:

Hotspot 的做者通過以往的研究發現大多數狀況下鎖不只不存在多線程競爭,並且老是由同一線程屢次得到。 偏向鎖的目的是在某個線程得到鎖以後,消除這個線程鎖重入(CAS)的開銷,看起來讓這個線程獲得了偏護。

引入偏向鎖是爲了在無多線程競爭的狀況下儘可能減小沒必要要的輕量級鎖執行路徑,由於輕量級鎖的獲取及釋放依賴屢次CAS原子指令, 而偏向鎖只須要在置換ThreadID的時候依賴一次CAS原子指令(因爲一旦出現多線程競爭的狀況就必須撤銷偏向鎖,因此偏向鎖的撤銷操做的性能損耗必須小於節省下來的CAS原子指令的性能消耗)。

上面說過, 輕量級鎖是爲了在線程交替執行同步塊時提升性能, 而偏向鎖則是在只有一個線程執行同步塊時進一步提升性能

5一、分段鎖

5二、鎖優化

5三、線程基本方法

5四、線程等待(wait)

5五、線程睡眠(sleep)

5六、線程讓步(yield)

5七、線程中斷(interrupt)

5八、Join等待其餘線程終止

5九、爲何要用join()方法?

答:

不少狀況下,主線程生成並啓動了子線程,須要用到子線程返回的結果,也就是須要主線程須要在子線程結束後再結束,這時候就要用到 join() 方法 。

System.out.println(Thread.currentThread().getName() + "線程運行開始!");
Thread6 thread1 = new Thread6();
thread1.setName("線程 B");
thread1.join();
System.out.println("這時 thread1 執行完畢以後才能執行主線程");

60、線程喚醒(notify)

6一、線程其餘方法

6二、進程

6三、上下文

6四、寄存器

6五、程序計數器

6六、PCB-「切換楨」

6七、上下文切換的活動

6八、引發線程上下文切換的緣由?

6九、同步鎖

70、死鎖

7一、線程池原理

7二、線程復

7三、線程池的組成

7四、拒絕策略

7五、Java線程池工做過程

7六、JAVA阻塞隊列原理

7七、Java中的阻塞隊列

7八、ArrayBlockingQueue(公平、非公平)

答:

用數組實現的有界阻塞隊列。此隊列按照先進先出(FIFO)的原則對元素進行排序。 默認狀況下不保證訪問者公平的訪問隊列,所謂公平訪問隊列是指阻塞的全部生產者線程或消費者線程,當隊列可用時,能夠按照阻塞的前後順序訪問隊列,即先阻塞的生產者線程,能夠先往隊列裏插入元素,先阻塞的消費者線程,能夠先從隊列裏獲取元素。一般狀況下爲了保證公平性會下降吞吐量。咱們可使用如下代碼建立一個公平的阻塞隊列

ArrayBlockingQueue fairQueue = new ArrayBlockingQueue(1000,true);

7九、LinkedBlockingQueue(兩個獨立鎖提升併發)

80、PriorityBlockingQueue(compareTo 排序實現優先)

8一、DelayQueue(緩存失效、定時任務 )

答:

是一個支持延時獲取元素的無界阻塞隊列。隊列使用PriorityQueue 來實現。隊列中的元素必須實現Delayed接口,在建立元素時能夠指定多久才能從隊列中獲取當前元素。只有在延遲期滿時才能從隊列中提取元素。咱們能夠將DelayQueue運用在如下應用場景:

  1. 緩存系統的設計:能夠用DelayQueue保存緩存元素的有效期,使用一個線程循環查詢DelayQueue,一旦能從DelayQueue 中獲取元素時,表示緩存有效期到了。

  2. 定時任務調度 :使用DelayQueue保存當天將會執行的任務和執行時間 ,一旦從DelayQueue中獲取到任務就開始執行,從好比TimerQueue就是使用DelayQueue實現的。

8二、SynchronousQueue(不存儲數據、可用於傳遞數據)

8三、LinkedTransferQueue

8四、LinkedBlockingDeque

8五、在 java中守護線程和本地線程區別

8六、線程與進程的區別?

答:

進程是操做系統分配資源的最小單元,線程是操做系統調度的最小單元。一個程序至少有一個進程,一個進程至少有一個線程。

8七、什麼是多線程中的上下文切換?

8八、死鎖與活鎖的區別,死鎖與飢餓的區別?

8九、Java 中用到的線程調度算法是什麼?

答:

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

90、什麼是線程組,爲何在Java中不推薦使用?

9一、爲何使用Executor框架?

9三、如何在Windows和Linux上查找哪一個線程使用的CPU時間最長?

9四、什麼是原子操做?在Java Concurrency API中有哪些原子類(atomic classes)?

9五、Java Concurrency API中的Lock接口(Lock interface)是什麼?對比同步它有什麼優點?

9六、什麼是Executors框架?

9七、什麼是阻塞隊列?阻塞隊列的實現原理是什麼?如何使用阻塞隊列來實現生產者-消費者模型?

9八、什麼是Callable和Future?

答:

Callable接口相似於Runnable,從名字就能夠看出來了,可是Runnable不會返回結果,而且沒法拋出返回結果的異常,而 Callable功能更強大一些,被線程執行後,能夠返回值,這個返回值能夠被Future拿到,也就是說,Future能夠拿到異步執行任務的返回值。能夠認爲是帶有回調的Runnable。

Future接口表示異步任務,是尚未完成的任務給出的將來結果。因此說Callable用於產生結果,Future用於獲取結果。

9九、什麼是FutureTask?使用ExecutorService啓動任務。

100、什麼是併發容器的實現?

10一、多線程同步和互斥有幾種實現方法,都是什麼?

10二、什麼是競爭條件?你怎樣發現和解決競爭?

10三、爲何咱們調用start()方法時會執行run()方法,爲何咱們不能直接調用run()方法?

10四、Java中你怎樣喚醒一個阻塞的線程?

10五、在Java中CycliBarriar和CountdownLatch 有什麼區別?

10六、什麼是不可變對象,它對寫併發應用有什麼幫助?

答:
不可變對象(Immutable Objects)即對象一旦被建立它的狀態(對象的數據,也即對象屬性值)就不能改變,反之即爲可變對象(MutableObjects)。不可變對象的類即爲不可變類(Immutable Class)。

Java平臺類庫中包含許多不可變類,如String、基本類型的包裝類、BigInteger和BigDecimal等。不可變對象天生是線程安全的。它們的常量(域)是在構造函數中建立的。既然它們的狀態沒法修改,這些常量永遠不會變。

不可變對象永遠是線程安全的。

只有知足以下狀態,一個對象纔是不可變的;它的狀態不能在建立後再被修改;全部域都是final類型;而且,它被正確建立(建立期間沒有發生this引用的逸出)。

10七、Java中用到的線程調度算法是什麼?

10八、什麼是線程組,爲何在Java中不推薦使用?

答:

線程組和線程池是兩個不一樣的概念,他們的做用徹底不一樣,前者是爲了方便線程的管理,後者是爲了管理線程的生命週期,複用線程,減小建立銷燬線程的開銷。

總結

該面試題答案解析完整文檔獲取方式:Java多線程&併發面試題總結

篇幅有限,其餘內容就不在這裏一 一展現了,整理不易,歡迎你們一塊兒交流,喜歡小編分享的文章記得關注我點贊喲,感謝支持!重要的事情說三遍,轉發+轉發+轉發,必定要記得轉發哦!!!