互聯(lián)網(wǎng) Java 工程師面試題(Java 并發(fā)編程二)

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

1、原子性

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

2、可見性

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

3、有序性

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

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

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

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

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

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

2、防止阻塞

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

3、便于建模

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

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

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

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

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

4、通過線程池創(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è)相同線 程來處理同一份資源的情況,從而可以將 CPU、代碼和數(shù)據(jù)分開,形成清晰的模 型,較好地體現(xiàn)了面向?qū)ο蟮乃枷搿?/p>

劣勢(shì)是:

編程稍微復(fù)雜,如果要訪問當(dāng)前線程,則必須使用 Thread.currentThread()方法。

2、使用繼承 Thread 類的方式創(chuàng)建多線程

優(yōu)勢(shì)是:

編寫簡(jiǎn)單,如果需要訪問當(dāng)前線程,則無需使用 Thread.currentThread()方法, 直接使用 this 即可獲得當(dāng)前線程。

劣勢(shì)是:

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

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

1、Callable 規(guī)定(重寫)的方法是 call(),Runnable 規(guī)定(重寫)的方法是 run()。

2、Callable 的任務(wù)執(zhí)行后可返回值,而 Runnable 的任務(wù)是不能返回值的。

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

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

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

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

![img_2.png][img_2.png]

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)的線程,只是說明此線程已經(jīng)做好了準(zhǔn)備,隨時(shí) 等待 CPU 調(diào)度執(zhí)行,并不是說執(zhí)行了 t.start()此線程立即就會(huì)執(zhí)行;

3、運(yùn)行狀態(tài)(Running):當(dāng) CPU 開始調(diào)度處于就緒狀態(tài)的線程時(shí),此時(shí)線程 才得以真正執(zhí)行,即進(jìn)入到運(yùn)行狀態(tài)。注:就 緒狀態(tài)是進(jìn)入到運(yùn)行狀態(tài)的唯一入 口,也就是說,線程要想進(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、其他阻塞:通過調(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í)候就可以考慮使用線程池來提升系統(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ì)用唯一的工 作線程來執(zhí)行任務(wù)。

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

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

2、可有效的控制最大并發(fā)線程數(shù),提高系統(tǒng)資源的使用率,同時(shí)避免過多資源競(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)單的說就是一個(gè)線程等待,直到他所等待的其他線程都執(zhí) 行完成并且調(diào)用 countDown()方法發(fā)出通知后,當(dāng)前線程才可以繼續(xù)執(zhí)行。

2、cyclicBarrier 是所有線程都進(jìn)行等待,直到所有線程都準(zhǔn)備好進(jìn)入 await()方 法之后,所有線程同時(shí)開始執(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 方法用來知道阻塞的線程是否被中斷。 如果被中斷返回 true,否則返回 false。

13、synchronized 的作用?

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

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

對(duì)于可見性,Java 提供了 volatile 關(guān)鍵字來保證可見性。

當(dāng)一個(gè)共享變量被 volatile 修飾時(shí),它會(huì)保證修改的值會(huì)立即被更新到主存,當(dāng) 有其他線程需要讀取時(shí),它會(huì)去內(nèi)存中讀取新值。

從實(shí)踐角度而言,volatile 的一個(gè)重要作用就是和 CAS 結(jié)合,保證了原子性,詳 細(xì)的可以參見 java.util.concurrent.atomic 包下的類,比如 AtomicInteger。

15、什么是 CAS

CAS 是 compare and swap 的縮寫,即我們所說的比較交換。

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

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

java.util.concurrent.atomic 包下的類大多是使用 CAS 操作來實(shí)現(xiàn)的 ( AtomicInteger,AtomicBoolean,AtomicLong)。

16、CAS 的問題

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

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

3、CAS 造成 CPU 利用率增加 之前說過了 CAS 里面是一個(gè)循環(huán)判斷的過程,如果線程一直沒有獲取到狀態(tài),cpu 資源會(huì)一直被占用。

17、什么是 Future?

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

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

18、什么是 AQS

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

AQS 是一個(gè)用來構(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 是什么

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

21、FutureTask 是什么

這個(gè)其實(shí)前面有提到過,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、什么是樂觀鎖和悲觀鎖

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

2、悲觀鎖:還是像它的名字一樣,對(duì)于并發(fā)間操作產(chǎ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 提供多線程共享變量可見性和禁止指令重排序優(yōu)化。

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

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

這個(gè)問題常問,sleep 方法和 wait 方法都可以用來放棄 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)無狀態(tài)的調(diào)用,特別適用于各個(gè)線程依賴不通的變量值完成操作的場(chǎng)景。 簡(jiǎn)單說 ThreadLocal 就是一種以空間換時(shí)間的做法,在每個(gè) Thread 里面維護(hù)了 一個(gè)以開地址法實(shí)現(xiàn)的 ThreadLocal.ThreadLocalMap,把數(shù)據(jù)進(jìn)行隔離,數(shù)據(jù) 不共享,自然就沒有線程安全方面的問題了。

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,這個(gè)前面有講過

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 并且沒有釋放的情況下去申請(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)致的阻塞,可以中斷線 程,并且通過拋出 InterruptedException 來喚醒它;如果線程遇到了 IO 阻塞, 無能為力,因?yàn)?IO 是操作系統(tǒng)實(shí)現(xiàn)的,Java 代碼并沒有辦法直接接觸到操作系統(tǒng)。

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

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

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

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

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

這里區(qū)分一下:

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

2、如果使用的是有界隊(duì)列比如 ArrayBlockingQueue,任務(wù)首先會(huì)被添加到 ArrayBlockingQueue 中,ArrayBlockingQueue 滿了,會(huì)根據(jù) maximumPoolSize 的值增加線程數(shù)量,如果增加了線程數(shù)量還是處理不過來, 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í)間分片(Time Slicing)?

線程調(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 線程的過程。分配 CPU 時(shí)間可 以基于線程優(yōu)先級(jí)或者線程等待的時(shí)間。線程調(diào)度并不受到 Java 虛擬機(jī)控制,所 以由應(yīng)用程序來控制它是更好的選擇(也就是說不要讓你的程序依賴于線程的優(yōu) 先級(jí))。

41、什么是自旋

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

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、可以讓線程嘗試獲取鎖,并在無法獲取鎖的時(shí)候立即返回或者等待一段時(shí)間

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

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

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

1、餓漢式單例模式的寫法:線程安全

2、懶漢式單例模式的寫法:非線程安全

3、雙檢鎖單例模式的寫法:線程安全

44、Semaphore 有什么作用

Semaphore 就是一個(gè)信號(hào)量,它的作用是限制某段代碼塊的并發(fā)數(shù)。Semaphor有一個(gè)構(gòu)造函數(shù),可以傳入一個(gè) int 型整數(shù) n,表示某段代碼最多只有 n 個(gè)線程可 以訪問,如果超出了 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è)非常刁鉆和狡猾的問題。請(qǐng)記?。壕€程類的構(gòu)造方法、靜態(tài)塊是被 new 這個(gè)線程類所在的線程所調(diào)用的,而 run 方法里面的代碼才是被線程自身所調(diào)用 的。

如果說上面的說法讓你感到困惑,那么我舉個(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ù)過多會(huì)造成什么異常?

1、線程的生命周期開銷非常高

2、消耗過多的 CPU 資源

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

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 異常

?著作權(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)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

  • 一、線程狀態(tài)轉(zhuǎn)換新建(New)可運(yùn)行(Runnable)阻塞(Blocking)無限期等待(Waiting)限期等...
    達(dá)微閱讀 678評(píng)論 1 2
  • Java并發(fā)編程 來自Java并發(fā)編程的藝術(shù)個(gè)人博客: http://blog.csdn.net/qq_22329...
    越長(zhǎng)越圓閱讀 3,340評(píng)論 4 54
  • 相關(guān)概念 面向?qū)ο蟮娜齻€(gè)特征 封裝,繼承,多態(tài).這個(gè)應(yīng)該是人人皆知.有時(shí)候也會(huì)加上抽象. 多態(tài)的好處 允許不同類對(duì)...
    東經(jīng)315度閱讀 2,192評(píng)論 0 8
  • 臭丫頭最近瘋狂的著迷數(shù)學(xué),而且今早主動(dòng)玩,給我出來一道1+2+3+.......31=?我的老天。我說這...
    靜雨逸橋閱讀 439評(píng)論 0 4
  • 前一陣和老師在群里布置了個(gè)作業(yè),讓大家寫寫對(duì)哪篇分享印象最深。一直記在心里想寫點(diǎn)東西,各種各樣的原因遲遲沒動(dòng)。 今...
    jewelliu閱讀 257評(píng)論 0 0

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