多線(xiàn)程面試題小結(jié)(值得收藏)

史上最強(qiáng)多線(xiàn)程面試47題(含答案),建議收藏
金九銀十快到了,即將進(jìn)入找工作的高峰期,最新整理的最全多線(xiàn)程并發(fā)面試47題和答案總結(jié),希望對(duì)想進(jìn)BAT的同學(xué)有幫助,由于篇幅較長(zhǎng),建議收藏后細(xì)看~
1、并發(fā)編程三要素?
1)原子性
原子性指的是一個(gè)或者多個(gè)操作,要么全部執(zhí)行并且在執(zhí)行的過(guò)程中不被其他操作打斷,要么就全部都不執(zhí)行。
2)可見(jiàn)性
可見(jiàn)性指多個(gè)線(xiàn)程操作一個(gè)共享變量時(shí),其中一個(gè)線(xiàn)程對(duì)變量進(jìn)行修改后,其他線(xiàn)程可以立即看到修改的結(jié)果。
3)有序性
有序性,即程序的執(zhí)行順序按照代碼的先后順序來(lái)執(zhí)行。
2、實(shí)現(xiàn)可見(jiàn)性的方法有哪些?
synchronized或者Lock:保證同一個(gè)時(shí)刻只有一個(gè)線(xiàn)程獲取鎖執(zhí)行代碼,鎖釋放之前把最新的值刷新到主內(nèi)存,實(shí)現(xiàn)可見(jiàn)性。
3、多線(xiàn)程的價(jià)值?
1)發(fā)揮多核CPU的優(yōu)勢(shì)
多線(xiàn)程,可以真正發(fā)揮出多核CPU的優(yōu)勢(shì)來(lái),達(dá)到充分利用CPU的目的,采用多線(xiàn)程的方式去同時(shí)完成幾件事情而不互相干擾。
2)防止阻塞
從程序運(yùn)行效率的角度來(lái)看,單核CPU不但不會(huì)發(fā)揮出多線(xiàn)程的優(yōu)勢(shì),反而會(huì)因?yàn)樵趩魏薈PU上運(yùn)行多線(xiàn)程導(dǎo)致線(xiàn)程上下文的切換,而降低程序整體的效率。但是單核CPU我們還是要應(yīng)用多線(xiàn)程,就是為了防止阻塞。試想,如果單核CPU使用單線(xiàn)程,那么只要這個(gè)線(xiàn)程阻塞了,比方說(shuō)遠(yuǎn)程讀取某個(gè)數(shù)據(jù)吧,對(duì)端遲遲未返回又沒(méi)有設(shè)置超時(shí)時(shí)間,那么你的整個(gè)程序在數(shù)據(jù)返回回來(lái)之前就停止運(yùn)行了。多線(xiàn)程可以防止這個(gè)問(wèn)題,多條線(xiàn)程同時(shí)運(yùn)行,哪怕一條線(xiàn)程的代碼執(zhí)行讀取數(shù)據(jù)阻塞,也不會(huì)影響其它任務(wù)的執(zhí)行。
3)便于建模
這是另外一個(gè)沒(méi)有這么明顯的優(yōu)點(diǎn)了。假設(shè)有一個(gè)大的任務(wù)A,單線(xiàn)程編程,那么就要考慮很多,建立整個(gè)程序模型比較麻煩。但是如果把這個(gè)大的任務(wù)A分解成幾個(gè)小任務(wù),任務(wù)B、任務(wù)C、任務(wù)D,分別建立程序模型,并通過(guò)多線(xiàn)程分別運(yùn)行這幾個(gè)任務(wù),那就簡(jiǎn)單很多了。
4、創(chuàng)建線(xiàn)程的有哪些方式?
1)繼承Thread類(lèi)創(chuàng)建線(xiàn)程類(lèi)
2)通過(guò)Runnable接口創(chuàng)建線(xiàn)程類(lèi)
3)通過(guò)Callable和Future創(chuàng)建線(xiàn)程
4)通過(guò)線(xiàn)程池創(chuàng)建
5、創(chuàng)建線(xiàn)程的三種方式的對(duì)比?
1)采用實(shí)現(xiàn)Runnable、Callable接口的方式創(chuàng)建多線(xiàn)程。
優(yōu)勢(shì)是:
線(xiàn)程類(lèi)只是實(shí)現(xiàn)了Runnable接口或Callable接口,還可以繼承其他類(lèi)。
在這種方式下,多個(gè)線(xiàn)程可以共享同一個(gè)target對(duì)象,所以非常適合多個(gè)相同線(xiàn)程來(lái)處理同一份資源的情況,從而可以將CPU、代碼和數(shù)據(jù)分開(kāi),形成清晰的模型,較好地體現(xiàn)了面向?qū)ο蟮乃枷搿?/p>
劣勢(shì)是:
編程稍微復(fù)雜,如果要訪(fǎng)問(wèn)當(dāng)前線(xiàn)程,則必須使用Thread.currentThread()方法。
2)使用繼承Thread類(lèi)的方式創(chuàng)建多線(xiàn)程
優(yōu)勢(shì)是:
編寫(xiě)簡(jiǎn)單,如果需要訪(fǎng)問(wèn)當(dāng)前線(xiàn)程,則無(wú)需使用Thread.currentThread()方法,直接使用this即可獲得當(dāng)前線(xiàn)程。
劣勢(shì)是:
線(xiàn)程類(lèi)已經(jīng)繼承了Thread類(lèi),所以不能再繼承其他父類(lèi)。
3)Runnable和Callable的區(qū)別
Callable規(guī)定(重寫(xiě))的方法是call(),Runnable規(guī)定(重寫(xiě))的方法是run()。
Callable的任務(wù)執(zhí)行后可返回值,而Runnable的任務(wù)是不能返回值的。
Call方法可以?huà)伋霎惓?,run方法不可以。
運(yùn)行Callable任務(wù)可以拿到一個(gè)Future對(duì)象,表示異步計(jì)算的結(jié)果。它提供了檢查計(jì)算是否完成的方法,以等待計(jì)算的完成,并檢索計(jì)算的結(jié)果。通過(guò)Future對(duì)象可以了解任務(wù)執(zhí)行情況,可取消任務(wù)的執(zhí)行,還可獲取執(zhí)行結(jié)果。
6、線(xiàn)程的狀態(tài)流轉(zhuǎn)圖
線(xiàn)程的生命周期及五種基本狀態(tài):
7、Java線(xiàn)程具有五中基本狀態(tài)
1)新建狀態(tài)(New):當(dāng)線(xiàn)程對(duì)象對(duì)創(chuàng)建后,即進(jìn)入了新建狀態(tài),如:Thread t = new MyThread();
2)就緒狀態(tài)(Runnable):當(dāng)調(diào)用線(xiàn)程對(duì)象的start()方法(t.start();),線(xiàn)程即進(jìn)入就緒狀態(tài)。處于就緒狀態(tài)的線(xiàn)程,只是說(shuō)明此線(xiàn)程已經(jīng)做好了準(zhǔn)備,隨時(shí)等待CPU調(diào)度執(zhí)行,并不是說(shuō)執(zhí)行了t.start()此線(xiàn)程立即就會(huì)執(zhí)行;
3)運(yùn)行狀態(tài)(Running):當(dāng)CPU開(kāi)始調(diào)度處于就緒狀態(tài)的線(xiàn)程時(shí),此時(shí)線(xiàn)程才得以真正執(zhí)行,即進(jìn)入到運(yùn)行狀態(tài)。注:就
緒狀態(tài)是進(jìn)入到運(yùn)行狀態(tài)的唯一入口,也就是說(shuō),線(xiàn)程要想進(jìn)入運(yùn)行狀態(tài)執(zhí)行,首先必須處于就緒狀態(tài)中;
4)阻塞狀態(tài)(Blocked):處于運(yùn)行狀態(tài)中的線(xiàn)程由于某種原因,暫時(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)又可以分為三種:
a.等待阻塞:運(yùn)行狀態(tài)中的線(xiàn)程執(zhí)行wait()方法,使本線(xiàn)程進(jìn)入到等待阻塞狀態(tài);
b.同步阻塞 – 線(xiàn)程在獲取synchronized同步鎖失敗(因?yàn)殒i被其它線(xiàn)程所占用),它會(huì)進(jìn)入同步阻塞狀態(tài);
c.其他阻塞 – 通過(guò)調(diào)用線(xiàn)程的sleep()或join()或發(fā)出了I/O請(qǐng)求時(shí),線(xiàn)程會(huì)進(jìn)入到阻塞狀態(tài)。當(dāng)sleep()狀態(tài)超時(shí)、join()等待線(xiàn)程終止或者超時(shí)、或者I/O處理完畢時(shí),線(xiàn)程重新轉(zhuǎn)入就緒狀態(tài)。
5)死亡狀態(tài)(Dead):線(xiàn)程執(zhí)行完了或者因異常退出了run()方法,該線(xiàn)程結(jié)束生命周期。
8、什么是線(xiàn)程池?有哪幾種創(chuàng)建方式?
線(xiàn)程池就是提前創(chuàng)建若干個(gè)線(xiàn)程,如果有任務(wù)需要處理,線(xiàn)程池里的線(xiàn)程就會(huì)處理任務(wù),處理完之后線(xiàn)程并不會(huì)被銷(xiāo)毀,而是等待下一個(gè)任務(wù)。由于創(chuàng)建和銷(xiāo)毀線(xiàn)程都是消耗系統(tǒng)資源的,所以當(dāng)你想要頻繁的創(chuàng)建和銷(xiāo)毀線(xiàn)程的時(shí)候就可以考慮使用線(xiàn)程池來(lái)提升系統(tǒng)的性能。
java 提供了一個(gè) java.util.concurrent.Executor接口的實(shí)現(xiàn)用于創(chuàng)建線(xiàn)程池。
9、四種線(xiàn)程池的創(chuàng)建:
1)newCachedThreadPool創(chuàng)建一個(gè)可緩存線(xiàn)程池
2)newFixedThreadPool 創(chuàng)建一個(gè)定長(zhǎng)線(xiàn)程池,可控制線(xiàn)程最大并發(fā)數(shù)。
3)newScheduledThreadPool 創(chuàng)建一個(gè)定長(zhǎng)線(xiàn)程池,支持定時(shí)及周期性任務(wù)執(zhí)行。
4)newSingleThreadExecutor 創(chuàng)建一個(gè)單線(xiàn)程化的線(xiàn)程池,它只會(huì)用唯一的工作線(xiàn)程來(lái)執(zhí)行任務(wù)。
10、線(xiàn)程池的優(yōu)點(diǎn)?
1)重用存在的線(xiàn)程,減少對(duì)象創(chuàng)建銷(xiāo)毀的開(kāi)銷(xiāo)。
2)可有效的控制最大并發(fā)線(xiàn)程數(shù),提高系統(tǒng)資源的使用率,同時(shí)避免過(guò)多資源競(jìng)爭(zhēng),避免堵塞。
3)提供定時(shí)執(zhí)行、定期執(zhí)行、單線(xiàn)程、并發(fā)數(shù)控制等功能。
11、常用的并發(fā)工具類(lèi)有哪些?
CountDownLatch
CyclicBarrier
Semaphore
Exchanger
12、CyclicBarrier和CountDownLatch的區(qū)別
1)CountDownLatch簡(jiǎn)單的說(shuō)就是一個(gè)線(xiàn)程等待,直到他所等待的其他線(xiàn)程都執(zhí)行完成并且調(diào)用countDown()方法發(fā)出通知后,當(dāng)前線(xiàn)程才可以繼續(xù)執(zhí)行。
2)cyclicBarrier是所有線(xiàn)程都進(jìn)行等待,直到所有線(xiàn)程都準(zhǔn)備好進(jìn)入await()方法之后,所有線(xiàn)程同時(shí)開(kāi)始執(zhí)行!
3)CountDownLatch的計(jì)數(shù)器只能使用一次。而CyclicBarrier的計(jì)數(shù)器可以使用reset() 方法重置。所以CyclicBarrier能處理更為復(fù)雜的業(yè)務(wù)場(chǎng)景,比如如果計(jì)算發(fā)生錯(cuò)誤,可以重置計(jì)數(shù)器,并讓線(xiàn)程們重新執(zhí)行一次。
4)CyclicBarrier還提供其他有用的方法,比如getNumberWaiting方法可以獲得CyclicBarrier阻塞的線(xiàn)程數(shù)量。isBroken方法用來(lái)知道阻塞的線(xiàn)程是否被中斷。如果被中斷返回true,否則返回false。
13、synchronized的作用?
在Java中,synchronized關(guān)鍵字是用來(lái)控制線(xiàn)程同步的,就是在多線(xiàn)程的環(huán)境下,控制synchronized代碼段不被多個(gè)線(xiàn)程同時(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)有其他線(xiàn)程需要讀取時(shí),它會(huì)去內(nèi)存中讀取新值。
從實(shí)踐角度而言,volatile的一個(gè)重要作用就是和CAS結(jié)合,保證了原子性,詳細(xì)的可以參見(jiàn)java.util.concurrent.atomic包下的類(lèi),比如AtomicInteger。
15、什么是CAS
CAS是compare and swap的縮寫(xiě),即我們所說(shuō)的比較交換。
cas是一種基于鎖的操作,而且是樂(lè)觀(guān)鎖。在java中鎖分為樂(lè)觀(guān)鎖和悲觀(guān)鎖。悲觀(guān)鎖是將資源鎖住,等一個(gè)之前獲得鎖的線(xiàn)程釋放鎖之后,下一個(gè)線(xiàn)程才可以訪(fǎng)問(wèn)。而樂(lè)觀(guān)鎖采取了一種寬泛的態(tài)度,通過(guò)某種方式不加鎖來(lái)處理資源,比如通過(guò)給記錄加version來(lái)獲取數(shù)據(jù),性能較悲觀(guān)鎖有很大的提高。
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線(xiàn)程獲取地址里面的值被b線(xiàn)程修改了,那么a線(xiàn)程需要自旋,到下次循環(huán)才有可能機(jī)會(huì)執(zhí)行。
java.util.concurrent.atomic 包下的類(lèi)大多是使用CAS操作來(lái)實(shí)現(xiàn)的( AtomicInteger,AtomicBoolean,AtomicLong)。
16、CAS的問(wèn)題
1)CAS容易造成ABA問(wèn)題
一個(gè)線(xiàn)程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ò)程,如果線(xiàn)程一直沒(méi)有獲取到狀態(tài),cpu資源會(huì)一直被占用。
17、什么是Future?
在并發(fā)編程中,我們經(jīng)常用到非阻塞的模型,在之前的多線(xiàn)程的三種實(shí)現(xiàn)中,不管是繼承thread類(lèi)還是實(shí)現(xiàn)runnable接口,都無(wú)法保證獲取到之前的執(zhí)行結(jié)果。通過(guò)實(shí)現(xiàn)Callback接口,并用Future可以來(lái)接收多線(xiàn)程的執(zhí)行結(jié)果。
Future表示一個(gè)可能還沒(méi)有完成的異步任務(wù)的結(jié)果,針對(duì)這個(gè)結(jié)果可以添加Callback以便在任務(wù)執(zhí)行成功或失敗后作出相應(yīng)的操作。
18、什么是AQS
AQS是AbustactQueuedSynchronizer的簡(jiǎn)稱(chēng),它是一個(gè)Java提高的底層同步工具類(lèi),用一個(gè)int類(lèi)型的變量表示同步狀態(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等等皆是基于A(yíng)QS的。
19、AQS支持兩種同步方式:
1)獨(dú)占式
2)共享式
這樣方便使用者實(shí)現(xiàn)不同類(lèi)型的同步組件,獨(dú)占式如ReentrantLock,共享式如Semaphore,CountDownLatch,組合式的如ReentrantReadWriteLock??傊?,AQS為使用提供了底層支撐,如何組裝實(shí)現(xiàn),使用者可以自由發(fā)揮。
20、ReadWriteLock是什么
首先明確一下,不是說(shuō)ReentrantLock不好,只是ReentrantLock某些時(shí)候有局限。如果使用ReentrantLock,可能本身是為了防止線(xiàn)程A在寫(xiě)數(shù)據(jù)、線(xiàn)程B在讀數(shù)據(jù)造成的數(shù)據(jù)不一致,但這樣,如果線(xiàn)程C在讀數(shù)據(jù)、線(xiàn)程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)類(lèi),可以對(duì)這個(gè)異步運(yùn)算的任務(wù)的結(jié)果進(jìn)行等待獲取、判斷是否已經(jīng)完成、取消任務(wù)等操作。當(dāng)然,由于FutureTask也是Runnable接口的實(shí)現(xiàn)類(lèi),所以FutureTask也可以放入線(xiàn)程池中。
22、synchronized和ReentrantLock的區(qū)別
synchronized是和if、else、for、while一樣的關(guān)鍵字,ReentrantLock是類(lèi),這是二者的本質(zhì)區(qū)別。既然ReentrantLock是類(lèi),那么它就提供了比synchronized更多更靈活的特性,可以被繼承、可以有方法、可以有各種各樣的類(lèi)變量,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è)觀(guān)鎖和悲觀(guān)鎖
1)樂(lè)觀(guān)鎖:就像它的名字一樣,對(duì)于并發(fā)間操作產(chǎn)生的線(xiàn)程安全問(wèn)題持樂(lè)觀(guān)狀態(tài),樂(lè)觀(guān)鎖認(rèn)為競(jìng)爭(zhēng)不總是會(huì)發(fā)生,因此它不需要持有鎖,將比較-替換這兩個(gè)動(dòng)作作為一個(gè)原子操作嘗試去修改內(nèi)存中的變量,如果失敗則表示發(fā)生沖突,那么就應(yīng)該有相應(yīng)的重試邏輯。
2)悲觀(guān)鎖:還是像它的名字一樣,對(duì)于并發(fā)間操作產(chǎn)生的線(xiàn)程安全問(wèn)題持悲觀(guān)狀態(tài),悲觀(guān)鎖認(rèn)為競(jìng)爭(zhēng)總是會(huì)發(fā)生,因此每次對(duì)某資源進(jìn)行操作時(shí),都會(huì)持有一個(gè)獨(dú)占的鎖,就像synchronized,不管三七二十一,直接上了鎖就操作資源了。
24、線(xiàn)程B怎么知道線(xiàn)程A修改了變量
volatile修飾變量
synchronized修飾修改變量的方法
wait/notify
while輪詢(xún)
25、synchronized、volatile、CAS比較
synchronized是悲觀(guān)鎖,屬于搶占式,會(huì)引起其他線(xiàn)程阻塞。
volatile提供多線(xiàn)程共享變量可見(jiàn)性和禁止指令重排序優(yōu)化。
CAS是基于沖突檢測(cè)的樂(lè)觀(guān)鎖(非阻塞)
26、sleep方法和wait方法有什么區(qū)別?
這個(gè)問(wèn)題常問(wèn),sleep方法和wait方法都可以用來(lái)放棄CPU一定的時(shí)間,不同點(diǎn)在于如果線(xiàn)程持有某個(gè)對(duì)象的監(jiān)視器,sleep方法不會(huì)放棄這個(gè)對(duì)象的監(jiān)視器,wait方法會(huì)放棄這個(gè)對(duì)象的監(jiān)視器
27、ThreadLocal是什么?有什么用?
ThreadLocal是一個(gè)本地線(xiàn)程副本變量工具類(lèi)。主要用于將私有線(xiàn)程和該線(xiàn)程存放的副本對(duì)象做一個(gè)映射,各個(gè)線(xiàn)程之間的變量互不干擾,在高并發(fā)場(chǎng)景下,可以實(shí)現(xiàn)無(wú)狀態(tài)的調(diào)用,特別適用于各個(gè)線(xiàn)程依賴(lài)不通的變量值完成操作的場(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)有線(xiàn)程安全方面的問(wèn)題了。
28、為什么wait()方法和notify()/notifyAll()方法要在同步塊中被調(diào)用
這是JDK強(qiáng)制的,wait()方法和notify()/notifyAll()方法在調(diào)用前都必須先獲得對(duì)象的鎖
29、多線(xiàn)程同步有哪幾種方法?
Synchronized關(guān)鍵字,Lock鎖實(shí)現(xiàn),分布式鎖等。
30、線(xiàn)程的調(diào)度策略
線(xiàn)程調(diào)度器選擇優(yōu)先級(jí)最高的線(xiàn)程運(yùn)行,但是,如果發(fā)生以下情況,就會(huì)終止線(xiàn)程的運(yùn)行:
1)線(xiàn)程體中調(diào)用了yield方法讓出了對(duì)cpu的占用權(quán)利
2)線(xiàn)程體中調(diào)用了sleep方法使線(xiàn)程進(jìn)入睡眠狀態(tài)
3)線(xiàn)程由于IO操作受到阻塞
4)另外一個(gè)更高優(yōu)先級(jí)線(xiàn)程出現(xiàn)
5)在支持時(shí)間片的系統(tǒng)中,該線(xiàn)程的時(shí)間片用完
31、ConcurrentHashMap的并發(fā)度是什么
ConcurrentHashMap的并發(fā)度就是segment的大小,默認(rèn)為16,這意味著最多同時(shí)可以有16條線(xiàn)程操作ConcurrentHashMap,這也是ConcurrentHashMap對(duì)Hashtable的最大優(yōu)勢(shì),任何情況下,Hashtable能同時(shí)有兩條線(xiàn)程獲取Hashtable中的數(shù)據(jù)嗎?
32、Linux環(huán)境下如何查找哪個(gè)線(xiàn)程使用CPU最長(zhǎng)
1)獲取項(xiàng)目的pid,jps或者ps -ef | grep java,這個(gè)前面有講過(guò)
2)top -H -p pid,順序不能改變
33、Java死鎖以及如何避免?
Java中的死鎖是一種編程情況,其中兩個(gè)或多個(gè)線(xiàn)程被永久阻塞,Java死鎖情況出現(xiàn)至少兩個(gè)線(xiàn)程和兩個(gè)或更多資源。
Java發(fā)生死鎖的根本原因是:在申請(qǐng)鎖時(shí)發(fā)生了交叉閉環(huán)申請(qǐng)。
34、死鎖的原因
1)是多個(gè)線(xiàn)程涉及到多個(gè)鎖,這些鎖存在著交叉,所以可能會(huì)導(dǎo)致了一個(gè)鎖依賴(lài)的閉環(huán)。
例如:線(xiàn)程在獲得了鎖A并且沒(méi)有釋放的情況下去申請(qǐng)鎖B,這時(shí),另一個(gè)線(xiàn)程已經(jīng)獲得了鎖B,在釋放鎖B之前又要先獲得鎖A,因此閉環(huán)發(fā)生,陷入死鎖循環(huán)。
2)默認(rèn)的鎖申請(qǐng)操作是阻塞的。
所以要避免死鎖,就要在一遇到多個(gè)對(duì)象鎖交叉的情況,就要仔細(xì)審查這幾個(gè)對(duì)象的類(lèi)中的所有方法,是否存在著導(dǎo)致鎖依賴(lài)的環(huán)路的可能性??傊潜M量避免在一個(gè)同步方法中調(diào)用其它對(duì)象的延時(shí)方法和同步方法。
35、怎么喚醒一個(gè)阻塞的線(xiàn)程
如果線(xiàn)程是因?yàn)檎{(diào)用了wait()、sleep()或者join()方法而導(dǎo)致的阻塞,可以中斷線(xiàn)程,并且通過(guò)拋出InterruptedException來(lái)喚醒它;如果線(xiàn)程遇到了IO阻塞,無(wú)能為力,因?yàn)镮O是操作系統(tǒng)實(shí)現(xiàn)的,Java代碼并沒(méi)有辦法直接接觸到操作系統(tǒng)。
36、不可變對(duì)象對(duì)多線(xiàn)程有什么幫助
前面有提到過(guò)的一個(gè)問(wèn)題,不可變對(duì)象保證了對(duì)象的內(nèi)存可見(jiàn)性,對(duì)不可變對(duì)象的讀取不需要進(jìn)行額外的同步手段,提升了代碼執(zhí)行效率。
37、什么是多線(xiàn)程的上下文切換
多線(xiàn)程的上下文切換是指CPU控制權(quán)由一個(gè)已經(jīng)正在運(yùn)行的線(xiàn)程切換到另外一個(gè)就緒并等待獲取CPU執(zhí)行權(quán)的線(xiàn)程的過(guò)程。
38、如果你提交任務(wù)時(shí),線(xiàn)程池隊(duì)列已滿(mǎn),這時(shí)會(huì)發(fā)生什么
這里區(qū)分一下:
1)如果使用的是無(wú)界隊(duì)列LinkedBlockingQueue,也就是無(wú)界隊(duì)列的話(huà),沒(méi)關(guān)系,繼續(xù)添加任務(wù)到阻塞隊(duì)列中等待執(zhí)行,因?yàn)長(zhǎng)inkedBlockingQueue可以近乎認(rèn)為是一個(gè)無(wú)窮大的隊(duì)列,可以無(wú)限存放任務(wù)
2)如果使用的是有界隊(duì)列比如ArrayBlockingQueue,任務(wù)首先會(huì)被添加到ArrayBlockingQueue中,ArrayBlockingQueue滿(mǎn)了,會(huì)根據(jù)maximumPoolSize的值增加線(xiàn)程數(shù)量,如果增加了線(xiàn)程數(shù)量還是處理不過(guò)來(lái),ArrayBlockingQueue繼續(xù)滿(mǎn),那么則會(huì)使用拒絕策略RejectedExecutionHandler處理滿(mǎn)了的任務(wù),默認(rèn)是AbortPolicy
39、Java中用到的線(xiàn)程調(diào)度算法是什么
搶占式。一個(gè)線(xiàn)程用完CPU之后,操作系統(tǒng)會(huì)根據(jù)線(xiàn)程優(yōu)先級(jí)、線(xiàn)程饑餓情況等數(shù)據(jù)算出一個(gè)總的優(yōu)先級(jí)并分配下一個(gè)時(shí)間片給某個(gè)線(xiàn)程執(zhí)行。
40、什么是線(xiàn)程調(diào)度器(Thread Scheduler)和時(shí)間分片(Time Slicing)?
線(xiàn)程調(diào)度器是一個(gè)操作系統(tǒng)服務(wù),它負(fù)責(zé)為Runnable狀態(tài)的線(xiàn)程分配CPU時(shí)間。一旦我們創(chuàng)建一個(gè)線(xiàn)程并啟動(dòng)它,它的執(zhí)行便依賴(lài)于線(xiàn)程調(diào)度器的實(shí)現(xiàn)。時(shí)間分片是指將可用的CPU時(shí)間分配給可用的Runnable線(xiàn)程的過(guò)程。分配CPU時(shí)間可以基于線(xiàn)程優(yōu)先級(jí)或者線(xiàn)程等待的時(shí)間。線(xiàn)程調(diào)度并不受到Java虛擬機(jī)控制,所以由應(yīng)用程序來(lái)控制它是更好的選擇(也就是說(shuō)不要讓你的程序依賴(lài)于線(xiàn)程的優(yōu)先級(jí))。
41、什么是自旋
很多synchronized里面的代碼只是一些很簡(jiǎn)單的代碼,執(zhí)行時(shí)間非常快,此時(shí)等待的線(xiàn)程都加鎖可能是一種不太值得的操作,因?yàn)榫€(xiàn)程阻塞涉及到用戶(hù)態(tài)和內(nèi)核態(tài)切換的問(wèn)題。既然synchronized里面的代碼執(zhí)行得非常快,不妨讓等待鎖的線(xiàn)程不要被阻塞,而是在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)類(lèi)的條件對(duì)象。
它的優(yōu)勢(shì)有:
可以使鎖更公平
可以使線(xiàn)程在等待鎖的時(shí)候響應(yīng)中斷
可以讓線(xiàn)程嘗試獲取鎖,并在無(wú)法獲取鎖的時(shí)候立即返回或者等待一段時(shí)間
可以在不同的范圍,以不同的順序獲取和釋放鎖
43、單例模式的線(xiàn)程安全性
老生常談的問(wèn)題了,首先要說(shuō)的是單例模式的線(xiàn)程安全意味著:某個(gè)類(lèi)的實(shí)例在多線(xiàn)程環(huán)境下只會(huì)被創(chuàng)建一次出來(lái)。單例模式有很多種的寫(xiě)法,我總結(jié)一下:
1)餓漢式單例模式的寫(xiě)法:線(xiàn)程安全
2)懶漢式單例模式的寫(xiě)法:非線(xiàn)程安全
3)雙檢鎖單例模式的寫(xiě)法:線(xiàn)程安全
44、Semaphore有什么作用
Semaphore就是一個(gè)信號(hào)量,它的作用是限制某段代碼塊的并發(fā)數(shù)。Semaphore有一個(gè)構(gòu)造函數(shù),可以傳入一個(gè)int型整數(shù)n,表示某段代碼最多只有n個(gè)線(xiàn)程可以訪(fǎng)問(wèn),如果超出了n,那么請(qǐng)等待,等到某個(gè)線(xiàn)程執(zhí)行完畢這段代碼塊,下一個(gè)線(xiàn)程再進(jìn)入。由此可以看出如果Semaphore構(gòu)造函數(shù)中傳入的int型整數(shù)n=1,相當(dāng)于變成了一個(gè)synchronized了。
45、Executors類(lèi)是什么?
Executors為Executor,ExecutorService,ScheduledExecutorService,ThreadFactory和Callable類(lèi)提供了一些工具方法。
Executors可以用于方便的創(chuàng)建線(xiàn)程池
46、線(xiàn)程類(lèi)的構(gòu)造方法、靜態(tài)塊是被哪個(gè)線(xiàn)程調(diào)用的
這是一個(gè)非常刁鉆和狡猾的問(wèn)題。請(qǐng)記?。壕€(xiàn)程類(lèi)的構(gòu)造方法、靜態(tài)塊是被new這個(gè)線(xiàn)程類(lèi)所在的線(xiàn)程所調(diào)用的,而run方法里面的代碼才是被線(xiàn)程自身所調(diào)用的。
如果說(shuō)上面的說(shuō)法讓你感到困惑,那么我舉個(gè)例子,假設(shè)Thread2中new了Thread1,main函數(shù)中new了Thread2,那么:
1)Thread2的構(gòu)造方法、靜態(tài)塊是main線(xiàn)程調(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線(xiàn)程數(shù)過(guò)多會(huì)造成什么異常?
1)線(xiàn)程的生命周期開(kāi)銷(xiāo)非常高
2)消耗過(guò)多的CPU資源
如果可運(yùn)行的線(xiàn)程數(shù)量多于可用處理器的數(shù)量,那么有線(xiàn)程將會(huì)被閑置。大量空閑的線(xiàn)程會(huì)占用許多內(nèi)存,給垃圾回收器帶來(lái)壓力,而且大量的線(xiàn)程在競(jìng)爭(zhēng)CPU資源時(shí)還將產(chǎn)生其他性能的開(kāi)銷(xiāo)。
3)降低穩(wěn)定性
JVM在可創(chuàng)建線(xiàn)程的數(shù)量上存在一個(gè)限制,這個(gè)限制值將隨著平臺(tái)的不同而不同,并且承受著多個(gè)因素制約,包括JVM的啟動(dòng)參數(shù)、Thread構(gòu)造函數(shù)中請(qǐng)求棧的大小,以及底層操作系統(tǒng)對(duì)線(xiàn)程的限制等。如果破壞了這些限制,那么可能拋出OutOfMemoryError異常。
到此這篇關(guān)于多線(xiàn)程面試題小結(jié)(值得收藏)的文章就介紹到這了,更多相關(guān)多線(xiàn)程面試題內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持腳本之家!
相關(guān)文章
這四十道阿里的多線(xiàn)程面試題,你能答對(duì)多少?(含答案)
這篇文章主要介紹了這四十道阿里的多線(xiàn)程面試題,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2020-02-1940個(gè)Java多線(xiàn)程問(wèn)題總結(jié)
這篇文章主要介紹了40個(gè)Java多線(xiàn)程問(wèn)題總結(jié),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2020-02-11- 這篇文章主要介紹了面試/筆試之多線(xiàn)程面試問(wèn)題集錦,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2020-02-06
Java多線(xiàn)程與并發(fā)面試題(小結(jié))
這篇文章主要介紹了Java多線(xiàn)程與并發(fā)面試題(小結(jié)),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2019-09-26史上最強(qiáng)多線(xiàn)程面試44題和答案:線(xiàn)程鎖+線(xiàn)程池+線(xiàn)程同步等
這篇文章主要介紹了史上最強(qiáng)多線(xiàn)程面試44題和答案:線(xiàn)程鎖+線(xiàn)程池+線(xiàn)程同步等,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2019-08-06