全網(wǎng)最全JAVA面試八股文,終于整理完了

前言

又到一年金三銀四面試跳槽季,你準(zhǔn)備好了嗎?

今天為大家整理了目前互聯(lián)網(wǎng)出現(xiàn)率最高的大廠面試題,所謂八股文也就是指文章的八個(gè)部分,文體有固定格式:由破題、承題、起講、入題、起股、中股、后股、束股八部分組成,題目一律出自四書(shū)五經(jīng)中的原文。

而JAVA面試八股文也就是為了考驗(yàn)大家的JAVA基礎(chǔ)功底,所以強(qiáng)烈建議背誦全文。

1、并發(fā)編程三要素?

(1) 原子性

原子性指的是一個(gè)或者多個(gè)操作,要么全部執(zhí)行并且在執(zhí)行的過(guò)程中不被其他操作打斷,要 么就全部都不執(zhí)行。

(2) 可見(jiàn)性

可見(jiàn)性指多個(gè)線程操作一個(gè)共享變量時(shí),其中一個(gè)線程對(duì)變量進(jìn)行修改后,其他線程可以立 即看到修改的結(jié)果。

(3) 有序性

有序性,即程序的執(zhí)行順序按照代碼的先后順序來(lái)執(zhí)行。

2、實(shí)現(xiàn)可見(jiàn)性的方法有哪些?

synchronized 或者 Lock:保證同一個(gè)時(shí)刻只有一個(gè)線程獲取鎖執(zhí)行代碼,鎖釋放之前把最新的值刷新到主內(nèi)存,實(shí)現(xiàn)可見(jiàn)性。

3、多線程的價(jià)值?

(1) 發(fā)揮多核 CPU 的優(yōu)勢(shì)

多線程,可以真正發(fā)揮出多核 CPU 的優(yōu)勢(shì)來(lái),達(dá)到充分利用 CPU 的目的,采用多線程的方式去同時(shí)完成幾件事情而不互相干擾。

(2) 防止阻塞

從程序運(yùn)行效率的角度來(lái)看,單核 CPU 不但不會(huì)發(fā)揮出多線程的優(yōu)勢(shì),反而會(huì)因?yàn)樵趩魏薈PU 上運(yùn)行多線程導(dǎo)致線程上下文的切換,而降低程序整體的效率。但是單核 CPU 我們還是要應(yīng)用多線程,就是為了防止阻塞。試想,如果單核 CPU 使用單線程,那么只要這個(gè)線程阻塞了,比方說(shuō)遠(yuǎn)程讀取某個(gè)數(shù)據(jù)吧,對(duì)端遲遲未返回又沒(méi)有設(shè)置超時(shí)時(shí)間,那么你的整個(gè)程序在數(shù)據(jù)返回回來(lái)之前就停止運(yùn)行了。多線程可以防止這個(gè)問(wèn)題,多條線程同時(shí)運(yùn)行, 哪怕一條線程的代碼執(zhí)行讀取數(shù)據(jù)阻塞,也不會(huì)影響其它任務(wù)的執(zhí)行。

(3) 便于建模

這是另外一個(gè)沒(méi)有這么明顯的優(yōu)點(diǎn)了。假設(shè)有一個(gè)大的任務(wù) A,單線程編程,那么就要考慮很多,建立整個(gè)程序模型比較麻煩。但是如果把這個(gè)大的任務(wù) A 分解成幾個(gè)小任務(wù),任務(wù) B、任務(wù) C、任務(wù) D,分別建立程序模型,并通過(guò)多線程分別運(yùn)行這幾個(gè)任務(wù),那就簡(jiǎn)單很多了。

4、創(chuàng)建線程的有哪些方式?

(1) 繼承 Thread 類創(chuàng)建線程類

(2) 通過(guò) Runnable 接口創(chuàng)建線程類

(3) 通過(guò) Callable 和 Future 創(chuàng)建線程

(4) 通過(guò)線程池創(chuàng)建

5、創(chuàng)建線程的三種方式的對(duì)比?

(1) 采用實(shí)現(xiàn) Runnable、Callable 接口的方式創(chuàng)建多線程。優(yōu)勢(shì)是:

線程類只是實(shí)現(xiàn)了 Runnable 接口或 Callable 接口,還可以繼承其他類。在這種方式下,多個(gè)線程可以共享同一個(gè) target 對(duì)象,所以非常適合多個(gè)相同線程來(lái)處理同一份資源的情況, 從而可以將 CPU、代碼和數(shù)據(jù)分開(kāi),形成清晰的模型,較好地體現(xiàn)了面向?qū)ο蟮乃枷搿?/p>

劣勢(shì)是:編程稍微復(fù)雜,如果要訪問(wèn)當(dāng)前線程,則必須使用 Thread.currentThread()方法。

(2) 使用繼承 Thread 類的方式創(chuàng)建多線程優(yōu)勢(shì)是:

編寫(xiě)簡(jiǎn)單,如果需要訪問(wèn)當(dāng)前線程,則無(wú)需使用 Thread.currentThread()方法,直接使用 this

即可獲得當(dāng)前線程。劣勢(shì)是:

線程類已經(jīng)繼承了 Thread 類,所以不能再繼承其他父類。

(3) Runnable 和 Callable 的區(qū)別

1、Callable 規(guī)定(重寫(xiě))的方法是 call(),Runnable 規(guī)定(重寫(xiě))的方法是 run()。2、Callable 的任務(wù)執(zhí)行后可返回值,而 Runnable 的任務(wù)是不能返回值的。

3、Call 方法可以拋出異常,run 方法不可以。

4、運(yùn)行 Callable 任務(wù)可以拿到一個(gè) Future 對(duì)象,表示異步計(jì)算的結(jié)果。它提供了檢查計(jì)算是否完成的方法,以等待計(jì)算的完成,并檢索計(jì)算的結(jié)果。通過(guò) Future 對(duì)象可以了解任務(wù)執(zhí)行情況,可取消任務(wù)的執(zhí)行,還可獲取執(zhí)行結(jié)果。

6、線程的狀態(tài)流轉(zhuǎn)圖

線程的生命周期及五種基本狀態(tài):

7、Java 線程具有五中基本狀態(tài)

(1) 新建狀態(tài)(New):

當(dāng)線程對(duì)象對(duì)創(chuàng)建后,即進(jìn)入了新建狀態(tài),如:Thread t= new MyThread();

(2) 就緒狀態(tài)(Runnable):

當(dāng)調(diào)用線程對(duì)象的 start()方法(t.start();),線程即進(jìn)入就緒狀態(tài)。處于就緒狀態(tài)的線程, 只是說(shuō)明此線程已經(jīng)做好了準(zhǔn)備,隨時(shí)等待 CPU 調(diào)度執(zhí)行,并不是說(shuō)執(zhí)行了 t.start()此線程立即就會(huì)執(zhí)行;

(3) 運(yùn)行狀態(tài)(Running):

當(dāng) CPU 開(kāi)始調(diào)度處于就緒狀態(tài)的線程時(shí),此時(shí)線程才得以真正執(zhí)行,即進(jìn)入到運(yùn)行狀態(tài)。注:就 緒狀態(tài)是進(jìn)入到運(yùn)行狀態(tài)的唯一入口,也就是說(shuō),線程要想進(jìn)入運(yùn)行狀態(tài)執(zhí)行,首先必須處于就緒狀態(tài)中;

(4) 阻塞狀態(tài)(Blocked):

處于運(yùn)行狀態(tài)中的線程由于某種原因,暫時(shí)放棄對(duì) CPU 的使用權(quán),停止執(zhí)行,此時(shí)進(jìn)入阻塞狀態(tài),直到其進(jìn)入到就緒狀態(tài),才 有機(jī)會(huì)再次被 CPU 調(diào)用以進(jìn)入到運(yùn)行狀態(tài)。根據(jù)阻塞產(chǎn)生的原因不同,阻塞狀態(tài)又可以分為三種:

1) 等待阻塞:運(yùn)行狀態(tài)中的線程執(zhí)行 wait()方法,使本線程進(jìn)入到等待阻塞狀態(tài);

2) 同步阻塞:線程在獲取 synchronized 同步鎖失敗(因?yàn)殒i被其它線程所占用), 它會(huì)進(jìn)入同步阻塞狀態(tài);

3) 其他阻塞:通過(guò)調(diào)用線程的 sleep()或 join()或發(fā)出了 I/O 請(qǐng)求時(shí),線程會(huì)進(jìn)入到阻塞狀態(tài)。當(dāng) sleep()狀態(tài)超時(shí)、join()等待線程終止或者超時(shí)、或者 I/O 處理完畢時(shí),線程重新轉(zhuǎn)入就緒狀態(tài)。

(5) 死亡狀態(tài)(Dead):

線程執(zhí)行完了或者因異常退出了 run()方法,該線程結(jié)束生命周期。

8、什么是線程池?有哪幾種創(chuàng)建方式?

線程池就是提前創(chuàng)建若干個(gè)線程,如果有任務(wù)需要處理,線程池里的線程就會(huì)處理任務(wù),處理完之后線程并不會(huì)被銷毀,而是等待下一個(gè)任務(wù)。由于創(chuàng)建和銷毀線程都是消耗系統(tǒng)資源的,所以當(dāng)你想要頻繁的創(chuàng)建和銷毀線程的時(shí)候就可以考慮使用線程池來(lái)提升系統(tǒng)的性能。

java 提供了一個(gè) java.util.concurrent.Executor 接口的實(shí)現(xiàn)用于創(chuàng)建線程池。

9、四種線程池的創(chuàng)建:

(1) newCachedThreadPool 創(chuàng)建一個(gè)可緩存線程池

(2) newFixedThreadPool 創(chuàng)建一個(gè)定長(zhǎng)線程池,可控制線程最大并發(fā)數(shù)。

(3) newScheduledThreadPool 創(chuàng)建一個(gè)定長(zhǎng)線程池,支持定時(shí)及周期性任務(wù)執(zhí)行。

(4) newSingleThreadExecutor 創(chuàng)建一個(gè)單線程化的線程池,它只會(huì)用唯一的工作線程來(lái)執(zhí)行任務(wù)。

10、線程池的優(yōu)點(diǎn)?

(1) 重用存在的線程,減少對(duì)象創(chuàng)建銷毀的開(kāi)銷。

(2) 可有效的控制最大并發(fā)線程數(shù),提高系統(tǒng)資源的使用率,同時(shí)避免過(guò)多資源競(jìng)爭(zhēng),避免堵塞。

(3) 提供定時(shí)執(zhí)行、定期執(zhí)行、單線程、并發(fā)數(shù)控制等功能。

11、常用的并發(fā)工具類有哪些?

(1) CountDownLatch

(2) CyclicBarrier

(3) Semaphore

(4) Exchanger

12、CyclicBarrier 和 CountDownLatch 的區(qū)別

(1) CountDownLatch 簡(jiǎn)單的說(shuō)就是一個(gè)線程等待,直到他所等待的其他線程都執(zhí)行完成并且調(diào)用 countDown()方法發(fā)出通知后,當(dāng)前線程才可以繼續(xù)執(zhí)行。

(2) cyclicBarrier 是所有線程都進(jìn)行等待,直到所有線程都準(zhǔn)備好進(jìn)入 await()方法之后, 所有線程同時(shí)開(kāi)始執(zhí)行!

(3) CountDownLatch 的計(jì)數(shù)器只能使用一次。而 CyclicBarrier 的計(jì)數(shù)器可以使用 reset() 方法重置。所以 CyclicBarrier 能處理更為復(fù)雜的業(yè)務(wù)場(chǎng)景,比如如果計(jì)算發(fā)生錯(cuò)誤,可以重置計(jì)數(shù)器,并讓線程們重新執(zhí)行一次。

( 4 ) CyclicBarrier 還提供其他有用的方法, 比如 getNumberWaiting 方法可以獲得CyclicBarrier 阻塞的線程數(shù)量。isBroken 方法用來(lái)知道阻塞的線程是否被中斷。如果被中斷返回 true,否則返回 false。

13、synchronized 的作用?

在 Java 中,synchronized 關(guān)鍵字是用來(lái)控制線程同步的,就是在多線程的環(huán)境下,控制synchronized 代碼段不被多個(gè)線程同時(shí)執(zhí)行。synchronized 既可以加在一段代碼上,也可以加在方法上。

14、volatile 關(guān)鍵字的作用

對(duì)于可見(jiàn)性,Java 提供了 volatile 關(guān)鍵字來(lái)保證可見(jiàn)性。當(dāng)一個(gè)共享變量被 volatile 修飾時(shí),它會(huì)保證修改的值會(huì)立即被更新到主存,當(dāng)有其他線程需要讀取時(shí),它會(huì)去內(nèi)存中讀取新值。從實(shí)踐角度而言,volatile 的一個(gè)重要作用就是和 CAS 結(jié)合,保證了原子性,詳細(xì)的可以參見(jiàn) java.util.concurrent.atomic 包下的類,比如 AtomicInteger。

15、什么是 CAS

CAS 是 compare and swap 的縮寫(xiě),即我們所說(shuō)的比較交換。

cas 是一種基于鎖的操作,而且是樂(lè)觀鎖。在 java 中鎖分為樂(lè)觀鎖和悲觀鎖。悲觀鎖是將資源鎖住,等一個(gè)之前獲得鎖的線程釋放鎖之后,下一個(gè)線程才可以訪問(wèn)。而樂(lè)觀鎖采取了一種寬泛的態(tài)度,通過(guò)某種方式不加鎖來(lái)處理資源,比如通過(guò)給記錄加 version 來(lái)獲取數(shù)據(jù), 性能較悲觀鎖有很大的提高。

CAS 操作包含三個(gè)操作數(shù) —— 內(nèi)存位置(V)、預(yù)期原值(A)和新值(B)。如果內(nèi)存地址里面的值和 A 的值是一樣的,那么就將內(nèi)存里面的值更新成 B。CAS 是通過(guò)無(wú)限循環(huán)來(lái)獲取數(shù)據(jù)的,若果在第一輪循環(huán)中,a 線程獲取地址里面的值被 b 線程修改了,那么 a 線程需要自旋,到下次循環(huán)才有可能機(jī)會(huì)執(zhí)行。

java.util.concurrent.atomic 包 下 的 類 大 多 是 使 用 CAS 操 作 來(lái) 實(shí) 現(xiàn) 的

(AtomicInteger,AtomicBoolean,AtomicLong)。

16、CAS 的問(wèn)題

(1) CAS 容易造成 ABA 問(wèn)題

一個(gè)線程 a 將數(shù)值改成了 b,接著又改成了 a,此時(shí) CAS 認(rèn)為是沒(méi)有變化,其實(shí)是已經(jīng)變化過(guò)了,而這個(gè)問(wèn)題的解決方案可以使用版本號(hào)標(biāo)識(shí),每操作一次 version 加 1。在 java5 中,已經(jīng)提供了 AtomicStampedReference 來(lái)解決問(wèn)題。

(2) 不能保證代碼塊的原子性

CAS 機(jī)制所保證的知識(shí)一個(gè)變量的原子性操作,而不能保證整個(gè)代碼塊的原子性。比如需要保證 3 個(gè)變量共同進(jìn)行原子性的更新,就不得不使用 synchronized 了。

(3) CAS 造成 CPU 利用率增加

之前說(shuō)過(guò)了 CAS 里面是一個(gè)循環(huán)判斷的過(guò)程,如果線程一直沒(méi)有獲取到狀態(tài),cpu 資源會(huì)一直被占用。

17、什么是 Future?

在并發(fā)編程中,我們經(jīng)常用到非阻塞的模型,在之前的多線程的三種實(shí)現(xiàn)中,不管是繼承thread 類還是實(shí)現(xiàn) runnable 接口,都無(wú)法保證獲取到之前的執(zhí)行結(jié)果。通過(guò)實(shí)現(xiàn) Callback 接口,并用 Future 可以來(lái)接收多線程的執(zhí)行結(jié)果。

Future 表示一個(gè)可能還沒(méi)有完成的異步任務(wù)的結(jié)果,針對(duì)這個(gè)結(jié)果可以添加 Callback 以便在任務(wù)執(zhí)行成功或失敗后作出相應(yīng)的操作。

18、什么是 AQS

AQS 是 AbustactQueuedSynchronizer 的簡(jiǎn)稱,它是一個(gè) Java 提高的底層同步工具類,用一個(gè) int 類型的變量表示同步狀態(tài),并提供了一系列的 CAS 操作來(lái)管理這個(gè)同步狀態(tài)。

AQS 是一個(gè)用來(lái)構(gòu)建鎖和同步器的框架,使用 AQS 能簡(jiǎn)單且高效地構(gòu)造出應(yīng)用廣泛的大量的同步器,比如我們提到的 ReentrantLock,Semaphore,其他的諸如 ReentrantReadWriteLock, SynchronousQueue,F(xiàn)utureTask 等等皆是基于 AQS 的。

19、AQS 支持兩種同步方式:

(1) 獨(dú)占式

(2) 共享式

這樣方便使用者實(shí)現(xiàn)不同類型的同步組件,獨(dú)占式如 ReentrantLock,共享式如 Semaphore, CountDownLatch,組 合 式 的 如 ReentrantReadWriteLock。總之,AQS 為使用提供了底層支撐,如何組裝實(shí)現(xiàn),使用者可以自由發(fā)揮。

20、ReadWriteLock 是什么

首先明確一下,不是說(shuō) ReentrantLock 不好,只是 ReentrantLock 某些時(shí)候有局限。如果使用 ReentrantLock,可能本身是為了防止線程 A 在寫(xiě)數(shù)據(jù)、線程 B 在讀數(shù)據(jù)造成的數(shù)據(jù)不一致,但這樣,如果線程 C 在讀數(shù)據(jù)、線程 D 也在讀數(shù)據(jù),讀數(shù)據(jù)是不會(huì)改變數(shù)據(jù)的, 沒(méi)有必要加鎖, 但是還是加鎖了, 降低了程序的性能。因?yàn)檫@個(gè), 才誕生了讀寫(xiě)鎖ReadWriteLock 。 ReadWriteLock 是 一 個(gè) 讀 寫(xiě) 鎖 接 口 , ReentrantReadWriteLock 是ReadWriteLock 接口的一個(gè)具體實(shí)現(xiàn),實(shí)現(xiàn)了讀寫(xiě)的分離,讀鎖是共享的,寫(xiě)鎖是獨(dú)占的, 讀和讀之間不會(huì)互斥,讀和寫(xiě)、寫(xiě)和讀、寫(xiě)和寫(xiě)之間才會(huì)互斥,提升了讀寫(xiě)的性能。

21、FutureTask 是什么

這個(gè)其實(shí)前面有提到過(guò),F(xiàn)utureTask 表示一個(gè)異步運(yùn)算的任務(wù)。FutureTask 里面可以傳入一個(gè) Callable 的具體實(shí)現(xiàn)類,可以對(duì)這個(gè)異步運(yùn)算的任務(wù)的結(jié)果進(jìn)行等待獲取、判斷是否已經(jīng)完成、取消任務(wù)等操作。當(dāng)然,由于 FutureTask 也是 Runnable 接口的實(shí)現(xiàn)類,所以FutureTask 也可以放入線程池中。

22、synchronized 和 ReentrantLock 的區(qū)別

synchronized 是和 if、else、for、while 一樣的關(guān)鍵字,ReentrantLock 是類,這是二者的本質(zhì)區(qū)別。既然 ReentrantLock 是類,那么它就提供了比 synchronized 更多更靈活的特性,可以被繼承、可以有方法、可以有各種各樣的類變量,ReentrantLock 比 synchronized 的擴(kuò)展性體現(xiàn)在幾點(diǎn)上:

(1) ReentrantLock 可以對(duì)獲取鎖的等待時(shí)間進(jìn)行設(shè)置,這樣就避免了死鎖

(2) ReentrantLock 可以獲取各種鎖的信息

(3) ReentrantLock 可以靈活地實(shí)現(xiàn)多路通知

另外,二者的鎖機(jī)制其實(shí)也是不一樣的。ReentrantLock 底層調(diào)用的是 Unsafe 的 park 方法加鎖,synchronized 操作的應(yīng)該是對(duì)象頭中 mark word,這點(diǎn)我不能確定。

23、什么是樂(lè)觀鎖和悲觀鎖

(1) 樂(lè)觀鎖:

就像它的名字一樣,對(duì)于并發(fā)間操作產(chǎn)生的線程安全問(wèn)題持樂(lè)觀狀態(tài),樂(lè)觀鎖認(rèn)為競(jìng)爭(zhēng)不總 是會(huì)發(fā)生,因此它不需要持有鎖,將比較-替換這兩個(gè)動(dòng)作作為一個(gè)原子操作嘗試去修改內(nèi)存中的變量,如果失敗則表示發(fā)生沖突,那么就應(yīng)該有相應(yīng)的重試邏輯。

(2) 悲觀鎖:

還是像它的名字一樣,對(duì)于并發(fā)間操作產(chǎn)生的線程安全問(wèn)題持悲觀狀態(tài),悲觀鎖認(rèn)為競(jìng)爭(zhēng)總 是會(huì)發(fā)生,因此每次對(duì)某資源進(jìn)行操作時(shí),都會(huì)持有一個(gè)獨(dú)占的鎖,就像 synchronized,不管三七二十一,直接上了鎖就操作資源了。

24、線程 B 怎么知道線程 A 修改了變量

(1) volatile 修飾變量

(2) synchronized 修飾修改變量的方法

(3) wait/notify

(4) while 輪詢

25、synchronized、volatile、CAS 比較

(1) synchronized 是悲觀鎖,屬于搶占式,會(huì)引起其他線程阻塞。

(2) volatile 提供多線程共享變量可見(jiàn)性和禁止指令重排序優(yōu)化。

(3) CAS 是基于沖突檢測(cè)的樂(lè)觀鎖(非阻塞)

26、sleep 方法和 wait 方法有什么區(qū)別?

這個(gè)問(wèn)題常問(wèn),sleep 方法和 wait 方法都可以用來(lái)放棄 CPU 一定的時(shí)間,不同點(diǎn)在于如果線程持有某個(gè)對(duì)象的監(jiān)視器,sleep 方法不會(huì)放棄這個(gè)對(duì)象的監(jiān)視器,wait 方法會(huì)放棄這個(gè)對(duì)象的監(jiān)視器

27、ThreadLocal 是什么?有什么用?

ThreadLocal 是一個(gè)本地線程副本變量工具類。主要用于將私有線程和該線程存放的副本對(duì)象做一個(gè)映射,各個(gè)線程之間的變量互不干擾,在高并發(fā)場(chǎng)景下,可以實(shí)現(xiàn)無(wú)狀態(tài)的調(diào)用, 特別適用于各個(gè)線程依賴不通的變量值完成操作的場(chǎng)景。簡(jiǎn)單說(shuō) ThreadLocal 就是一種以空間 換 時(shí) 間 的 做 法 , 在 每 個(gè) Thread 里 面 維 護(hù) 了 一 個(gè) 以 開(kāi) 地 址 法 實(shí) 現(xiàn) 的ThreadLocal.ThreadLocalMap,把數(shù)據(jù)進(jìn)行隔離,數(shù)據(jù)不共享,自然就沒(méi)有線程安全方面的問(wèn)題了。

28、為什么 wait()方法和 notify()/notifyAll()方法要在同步塊中被調(diào)用

這是 JDK 強(qiáng)制的,wait()方法和 notify()/notifyAll()方法在調(diào)用前都必須先獲得對(duì)象的鎖

29、多線程同步有哪幾種方法?

Synchronized 關(guān)鍵字,Lock 鎖實(shí)現(xiàn),分布式鎖等。

30、線程的調(diào)度策略

線程調(diào)度器選擇優(yōu)先級(jí)最高的線程運(yùn)行,但是,如果發(fā)生以下情況,就會(huì)終止線程的運(yùn)行:

(1) 線程體中調(diào)用了 yield 方法讓出了對(duì) cpu 的占用權(quán)利

(2) 線程體中調(diào)用了 sleep 方法使線程進(jìn)入睡眠狀態(tài)

(3) 線程由于 IO 操作受到阻塞

(4) 另外一個(gè)更高優(yōu)先級(jí)線程出現(xiàn)

(5) 在支持時(shí)間片的系統(tǒng)中,該線程的時(shí)間片用完

31、ConcurrentHashMap 的并發(fā)度是什么

ConcurrentHashMap 的并發(fā)度就是 segment 的大小,默認(rèn)為 16,這意味著最多同時(shí)可以有16 條線程操作 ConcurrentHashMap,這也是 ConcurrentHashMap 對(duì) Hashtable 的最大優(yōu)勢(shì),任何情況下,Hashtable 能同時(shí)有兩條線程獲取 Hashtable 中的數(shù)據(jù)嗎?

32、Linux 環(huán)境下如何查找哪個(gè)線程使用 CPU 最長(zhǎng)

(1) 獲取項(xiàng)目的 pid,jps 或者 ps -ef | grep java

(2) top -H -p pid,順序不能改變

33、Java 死鎖以及如何避免?

Java 中的死鎖是一種編程情況,其中兩個(gè)或多個(gè)線程被永久阻塞,Java 死鎖情況出現(xiàn)至少兩個(gè)線程和兩個(gè)或更多資源。

Java 發(fā)生死鎖的根本原因是:在申請(qǐng)鎖時(shí)發(fā)生了交叉閉環(huán)申請(qǐng)。

34、死鎖的原因

(1) 是多個(gè)線程涉及到多個(gè)鎖,這些鎖存在著交叉,所以可能會(huì)導(dǎo)致了一個(gè)鎖依賴的閉環(huán)。例如:線程在獲得了鎖 A 并且沒(méi)有釋放的情況下去申請(qǐng)鎖 B,這時(shí),另一個(gè)線程已經(jīng)獲得了鎖 B,在釋放鎖 B 之前又要先獲得鎖 A,因此閉環(huán)發(fā)生,陷入死鎖循環(huán)。

(2) 默認(rèn)的鎖申請(qǐng)操作是阻塞的。

所以要避免死鎖,就要在一遇到多個(gè)對(duì)象鎖交叉的情況,就要仔細(xì)審查這幾個(gè)對(duì)象的類中的 所有方法,是否存在著導(dǎo)致鎖依賴的環(huán)路的可能性??傊潜M量避免在一個(gè)同步方法中調(diào)用 其它對(duì)象的延時(shí)方法和同步方法。

35、怎么喚醒一個(gè)阻塞的線程

如果線程是因?yàn)檎{(diào)用了 wait()、sleep()或 者 join()方法而導(dǎo)致的阻塞,可以中斷線程,并且通過(guò)拋出 InterruptedException 來(lái)喚醒它;如果線程遇到了 IO 阻塞,無(wú)能為力,因?yàn)?IO 是操作系統(tǒng)實(shí)現(xiàn)的,Java 代碼并沒(méi)有辦法直接接觸到操作系統(tǒng)。

36、不可變對(duì)象對(duì)多線程有什么幫助

前面有提到過(guò)的一個(gè)問(wèn)題,不可變對(duì)象保證了對(duì)象的內(nèi)存可見(jiàn)性,對(duì)不可變對(duì)象的讀取不需 要進(jìn)行額外的同步手段,提升了代碼執(zhí)行效率。

37、什么是多線程的上下文切換

多線程的上下文切換是指 CPU 控制權(quán)由一個(gè)已經(jīng)正在運(yùn)行的線程切換到另外一個(gè)就緒并等待獲取 CPU 執(zhí)行權(quán)的線程的過(guò)程。

38、如果你提交任務(wù)時(shí),線程池隊(duì)列已滿,這時(shí)會(huì)發(fā)生什么

這里區(qū)分一下:

(1)如果使用的是無(wú)界隊(duì)列 LinkedBlockingQueue,也就是無(wú)界隊(duì)列的話,沒(méi)關(guān)系,繼續(xù)添加任務(wù)到阻塞隊(duì)列中等待執(zhí)行,因?yàn)?LinkedBlockingQueue 可以近乎認(rèn)為是一個(gè)無(wú)窮大的隊(duì)列,可以無(wú)限存放任務(wù)

( 2 ) 如果 使 用的 是 有界 隊(duì) 列 比如 ArrayBlockingQueue , 任務(wù) 首 先會(huì) 被 添加 到ArrayBlockingQueue 中,ArrayBlockingQueue 滿了,會(huì)根據(jù) maximumPoolSize 的值增加線程數(shù)量,如果增加了線程數(shù)量還是處理不過(guò)來(lái),ArrayBlockingQueue 繼續(xù)滿,那么則會(huì)使用拒絕策略 RejectedExecutionHandler 處理滿了的任務(wù),默認(rèn)是 AbortPolicy

39、Java 中用到的線程調(diào)度算法是什么

搶占式。一個(gè)線程用完 CPU 之后,操作系統(tǒng)會(huì)根據(jù)線程優(yōu)先級(jí)、線程饑餓情況等數(shù)據(jù)算出一個(gè)總的優(yōu)先級(jí)并分配下一個(gè)時(shí)間片給某個(gè)線程執(zhí)行。

40、什么是線程調(diào)度器(Thread Scheduler)和時(shí)間分片(TimeSlicing)?

線程調(diào)度器是一個(gè)操作系統(tǒng)服務(wù),它負(fù)責(zé)為 Runnable 狀態(tài)的線程分配 CPU 時(shí)間。一旦我們創(chuàng)建一個(gè)線程并啟動(dòng)它,它的執(zhí)行便依賴于線程調(diào)度器的實(shí)現(xiàn)。時(shí)間分片是指將可用的CPU 時(shí)間分配給可用的 Runnable 線程的過(guò)程。分配 CPU 時(shí)間可以基于線程優(yōu)先級(jí)或者線程等待的時(shí)間。線程調(diào)度并不受到 Java 虛擬機(jī)控制,所以由應(yīng)用程序來(lái)控制它是更好的選擇(也就是說(shuō)不要讓你的程序依賴于線程的優(yōu)先級(jí))。

41、什么是自旋

很多 synchronized 里面的代碼只是一些很簡(jiǎn)單的代碼,執(zhí)行時(shí)間非???,此時(shí)等待的線程

都加鎖可能是一種不太值得的操作,因?yàn)榫€程阻塞涉及到用戶態(tài)和內(nèi)核態(tài)切換的問(wèn)題。既然synchronized 里面的代碼執(zhí)行得非???, 不妨讓等待鎖的線程不要被阻塞, 而是在synchronized 的邊界做忙循環(huán),這就是自旋。如果做了多次忙循環(huán)發(fā)現(xiàn)還沒(méi)有獲得鎖,再阻塞,這樣可能是一種更好的策略。

42、Java Concurrency API 中的 Lock 接口(Lock interface)是什么?對(duì)比同步它有什么優(yōu)勢(shì)?

Lock 接口比同步方法和同步塊提供了更具擴(kuò)展性的鎖操作。他們?cè)试S更靈活的結(jié)構(gòu),可以具有完全不同的性質(zhì),并且可以支持多個(gè)相關(guān)類的條件對(duì)象。

它的優(yōu)勢(shì)有:

(1) 可以使鎖更公平

(2) 可以使線程在等待鎖的時(shí)候響應(yīng)中斷

(3) 可以讓線程嘗試獲取鎖,并在無(wú)法獲取鎖的時(shí)候立即返回或者等待一段時(shí)間

(4) 可以在不同的范圍,以不同的順序獲取和釋放鎖

43、單例模式的線程安全性

老生常談的問(wèn)題了,首先要說(shuō)的是單例模式的線程安全意味著:某個(gè)類的實(shí)例在多線程環(huán)境 下只會(huì)被創(chuàng)建一次出來(lái)。單例模式有很多種的寫(xiě)法,我總結(jié)一下:

(1) 餓漢式單例模式的寫(xiě)法:線程安全

(2) 懶漢式單例模式的寫(xiě)法:非線程安全

(3) 雙檢鎖單例模式的寫(xiě)法:線程安全

44、Semaphore 有什么作用

Semaphore 就是一個(gè)信號(hào)量,它的作用是限制某段代碼塊的并發(fā)數(shù)。Semaphore 有一個(gè)構(gòu)造函數(shù),可以傳入一個(gè) int 型整數(shù) n,表示某段代碼最多只有 n 個(gè)線程可以訪問(wèn),如果超出了 n,那么請(qǐng)等待,等到某個(gè)線程執(zhí)行完畢這段代碼塊,下一個(gè)線程再進(jìn)入。由此可以看出如果 Semaphore 構(gòu)造函數(shù)中傳入的 int 型整數(shù) n=1,相當(dāng)于變成了一個(gè) synchronized 了。

45、Executors 類是什么?

Executors 為 Executor,ExecutorService,ScheduledExecutorService,ThreadFactory 和 Callable

類提供了一些工具方法。Executors 可以用于方便的創(chuàng)建線程池

46、線程類的構(gòu)造方法、靜態(tài)塊是被哪個(gè)線程調(diào)用的

這是一個(gè)非常刁鉆和狡猾的問(wèn)題。請(qǐng)記?。壕€程類的構(gòu)造方法、靜態(tài)塊是被 new 這個(gè)線程類所在的線程所調(diào)用的,而 run 方法里面的代碼才是被線程自身所調(diào)用的。

如果說(shuō)上面的說(shuō)法讓你感到困惑,那么我舉個(gè)例子,假設(shè) Thread2 中 new 了 Thread1,main

函數(shù)中 new 了 Thread2,那么:

(1) Thread2 的構(gòu)造方法、靜態(tài)塊是 main 線程調(diào)用的,Thread2 的 run()方法是 Thread2 自己調(diào)用的

(2) Thread1 的構(gòu)造方法、靜態(tài)塊是 Thread2 調(diào)用的,Thread1 的 run()方法是 Thread1 自己調(diào)用的

47、同步方法和同步塊,哪個(gè)是更好的選擇?

同步塊,這意味著同步塊之外的代碼是異步執(zhí)行的,這比同步整個(gè)方法更提升代碼的效率。 請(qǐng)知道一條原則:同步的范圍越小越好。

48、Java 線程數(shù)過(guò)多會(huì)造成什么異常?

(1) 線程的生命周期開(kāi)銷非常高

(2) 消耗過(guò)多的 CPU 資源

如果可運(yùn)行的線程數(shù)量多于可用處理器的數(shù)量,那么有線程將會(huì)被閑置。大量空閑的線程會(huì) 占用許多內(nèi)存,給垃圾回收器帶來(lái)壓力,而且大量的線程在競(jìng)爭(zhēng) CPU 資源時(shí)還將產(chǎn)生其他性能的開(kāi)銷。

(3) 降低穩(wěn)定性

JVM 在可創(chuàng)建線程的數(shù)量上存在一個(gè)限制,這個(gè)限制值將隨著平臺(tái)的不同而不同,并且承受著多個(gè)因素制約,包括 JVM 的啟動(dòng)參數(shù)、Thread 構(gòu)造函數(shù)中請(qǐng)求棧的大小,以及底層操作系統(tǒng)對(duì)線程的限制等。如果破壞了這些限制,那么可能拋出 OutOfMemoryError 異常。

總結(jié)

由于篇幅內(nèi)容有限,本次分享就到這里了!如過(guò)文章對(duì)你有幫助,不要忘記點(diǎn)贊收藏關(guān)注哦!

最后也為大家準(zhǔn)備好了《金三年銀四JAVA面試題》私信我即可領(lǐng)取,祝大家都能早日升職加薪!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容