最近面試的小夥伴不少,對此我整理了一份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面試題總結 | 未更新 |
答:
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();
答:算法
若是本身的類已經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(); } }
答:
有返回值的任務必須實現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()); }
答:
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()返回一個線程池(這個線程池只有一個線程),這個線程池能夠在線程死後(或發生異常時)從新啓動一個線程來替代原來的線程繼續執行下去!
答:
一旦一個共享變量(類的成員變量、類的靜態成員變量)被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 通常用於狀態標記量和單例模式的雙檢鎖。
答:
在多線程中有多種方法讓線程按特定順序執行,你能夠用線程類的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(); } }
答:
若是問到了這樣的問題,能夠展開的說一下線程池如何用、線程池的好處、線程池的啓動策略)合理利用線程池可以帶來三個好處。
第一:下降資源消耗。經過重複利用已建立的線程下降線程建立和銷燬形成的消耗。
第二:提升響應速度。當任務到達時,任務能夠不須要等到線程建立就能當即執行。
第三:提升線程的可管理性。線程是稀缺資源,若是無限制的建立,不只會消耗系統資源,還會下降系統的穩定性,使用線程池能夠進行統一的分配,調優和監控。
答:
Semaphore 是一種基於計數的信號量。它能夠設定一個閾值,基於此,多個線程競爭獲取許可信號,作完本身的申請後歸還,超過閾值後,線程申請許可信號將會被阻塞。 Semaphore 能夠用來構建一些對象池,資源池之類的, 好比數據庫鏈接池
實現互斥鎖(計數器爲 1)咱們也能夠建立計數爲 1 的 Semaphore,將其做爲一種相似互斥鎖的機制,這也叫二元信號量,
表示兩種互斥狀態。
代碼實現
// 建立一個計數閾值 // 只能 5 個線程同時訪問 Semaphore semp = new Semaphore(5); try { // 申請許可 semp.acquire(); try { // 業務邏輯 } catch (Exception e) { } finally { // 釋放許可semp.release(); } } catch (InterruptedException e) { }
答:
Hotspot 的做者通過以往的研究發現大多數狀況下鎖不只不存在多線程競爭,並且老是由同一線程屢次得到。 偏向鎖的目的是在某個線程得到鎖以後,消除這個線程鎖重入(CAS)的開銷,看起來讓這個線程獲得了偏護。
引入偏向鎖是爲了在無多線程競爭的狀況下儘可能減小沒必要要的輕量級鎖執行路徑,由於輕量級鎖的獲取及釋放依賴屢次CAS原子指令, 而偏向鎖只須要在置換ThreadID的時候依賴一次CAS原子指令(因爲一旦出現多線程競爭的狀況就必須撤銷偏向鎖,因此偏向鎖的撤銷操做的性能損耗必須小於節省下來的CAS原子指令的性能消耗)。
上面說過, 輕量級鎖是爲了在線程交替執行同步塊時提升性能, 而偏向鎖則是在只有一個線程執行同步塊時進一步提升性能
答:
不少狀況下,主線程生成並啓動了子線程,須要用到子線程返回的結果,也就是須要主線程須要在子線程結束後再結束,這時候就要用到 join() 方法 。
System.out.println(Thread.currentThread().getName() + "線程運行開始!"); Thread6 thread1 = new Thread6(); thread1.setName("線程 B"); thread1.join(); System.out.println("這時 thread1 執行完畢以後才能執行主線程");
答:
用數組實現的有界阻塞隊列。此隊列按照先進先出(FIFO)的原則對元素進行排序。 默認狀況下不保證訪問者公平的訪問隊列,所謂公平訪問隊列是指阻塞的全部生產者線程或消費者線程,當隊列可用時,能夠按照阻塞的前後順序訪問隊列,即先阻塞的生產者線程,能夠先往隊列裏插入元素,先阻塞的消費者線程,能夠先從隊列裏獲取元素。一般狀況下爲了保證公平性會下降吞吐量。咱們可使用如下代碼建立一個公平的阻塞隊列
ArrayBlockingQueue fairQueue = new ArrayBlockingQueue(1000,true);
答:
是一個支持延時獲取元素的無界阻塞隊列。隊列使用PriorityQueue 來實現。隊列中的元素必須實現Delayed接口,在建立元素時能夠指定多久才能從隊列中獲取當前元素。只有在延遲期滿時才能從隊列中提取元素。咱們能夠將DelayQueue運用在如下應用場景:
緩存系統的設計:能夠用DelayQueue保存緩存元素的有效期,使用一個線程循環查詢DelayQueue,一旦能從DelayQueue 中獲取元素時,表示緩存有效期到了。
定時任務調度 :使用DelayQueue保存當天將會執行的任務和執行時間 ,一旦從DelayQueue中獲取到任務就開始執行,從好比TimerQueue就是使用DelayQueue實現的。
答:
進程是操做系統分配資源的最小單元,線程是操做系統調度的最小單元。一個程序至少有一個進程,一個進程至少有一個線程。
答:
採用時間片輪轉的方式。能夠設置線程的優先級,會映射到下層的系統上面的優先級上,如非特別須要,儘可能不要用,防止線程飢餓。
答:
Callable接口相似於Runnable,從名字就能夠看出來了,可是Runnable不會返回結果,而且沒法拋出返回結果的異常,而 Callable功能更強大一些,被線程執行後,能夠返回值,這個返回值能夠被Future拿到,也就是說,Future能夠拿到異步執行任務的返回值。能夠認爲是帶有回調的Runnable。
Future接口表示異步任務,是尚未完成的任務給出的將來結果。因此說Callable用於產生結果,Future用於獲取結果。
答:
不可變對象(Immutable Objects)即對象一旦被建立它的狀態(對象的數據,也即對象屬性值)就不能改變,反之即爲可變對象(MutableObjects)。不可變對象的類即爲不可變類(Immutable Class)。
Java平臺類庫中包含許多不可變類,如String、基本類型的包裝類、BigInteger和BigDecimal等。不可變對象天生是線程安全的。它們的常量(域)是在構造函數中建立的。既然它們的狀態沒法修改,這些常量永遠不會變。
不可變對象永遠是線程安全的。
只有知足以下狀態,一個對象纔是不可變的;它的狀態不能在建立後再被修改;全部域都是final類型;而且,它被正確建立(建立期間沒有發生this引用的逸出)。
答:
線程組和線程池是兩個不一樣的概念,他們的做用徹底不一樣,前者是爲了方便線程的管理,後者是爲了管理線程的生命週期,複用線程,減小建立銷燬線程的開銷。
該面試題答案解析完整文檔獲取方式:Java多線程&併發面試題總結
篇幅有限,其餘內容就不在這裏一 一展現了,整理不易,歡迎你們一塊兒交流,喜歡小編分享的文章記得關注我點贊喲,感謝支持!重要的事情說三遍,轉發+轉發+轉發,必定要記得轉發哦!!!