1. 線(xiàn)程和進(jìn)程的區(qū)別?
它們是不同的操作系統(tǒng)資源管理方式。進(jìn)程有獨(dú)立的地址空間,一個(gè)進(jìn)程崩潰后,在保護(hù)模式下不會(huì)對(duì)其它進(jìn)程產(chǎn)生影響,而線(xiàn)程只是一個(gè)進(jìn)程中的不同執(zhí)行路徑。線(xiàn)程有自己的堆棧和局部變量,但線(xiàn)程之間沒(méi)有單獨(dú)的地址空間,一個(gè)線(xiàn)程死掉就等于整個(gè)進(jìn)程死掉,所以多進(jìn)程的程序要比多線(xiàn)程的程序健壯,但在進(jìn)程切換時(shí),耗費(fèi)資源較大,效率要差一些。但對(duì)于一些要求同時(shí)進(jìn)行并且又要共享某些變量的并發(fā)操作,只能用線(xiàn)程,不能用進(jìn)程。
- 簡(jiǎn)而言之,一個(gè)程序至少有一個(gè)進(jìn)程,一個(gè)進(jìn)程至少有一個(gè)線(xiàn)程.
- 線(xiàn)程的劃分尺度小于進(jìn)程,使得多線(xiàn)程程序的并發(fā)性高。
- 另外,進(jìn)程在執(zhí)行過(guò)程中擁有獨(dú)立的內(nèi)存單元,而多個(gè)線(xiàn)程共享內(nèi)存,從而極大地提高了程序的運(yùn)行效率。
- 線(xiàn)程在執(zhí)行過(guò)程中與進(jìn)程還是有區(qū)別的。每個(gè)獨(dú)立的線(xiàn)程有一個(gè)程序運(yùn)行的入口、順序執(zhí)行序列和程序的出口。但是線(xiàn)程不能夠獨(dú)立執(zhí)行,必須依存在應(yīng)用程序中,由應(yīng)用程序提供多個(gè)線(xiàn)程執(zhí)行控制。
- 從邏輯角度來(lái)看,多線(xiàn)程的意義在于一個(gè)應(yīng)用程序中,有多個(gè)執(zhí)行部分可以同時(shí)執(zhí)行。但操作系統(tǒng)并沒(méi)有將多個(gè)線(xiàn)程看做多個(gè)獨(dú)立的應(yīng)用,來(lái)實(shí)現(xiàn)進(jìn)程的調(diào)度和管理以及資源分配。這就是進(jìn)程和線(xiàn)程的重要區(qū)別。
2. 實(shí)現(xiàn)線(xiàn)程有哪幾種方式?
(1)實(shí)現(xiàn)Runnable接口
(2)繼承Thread類(lèi)
(3)通過(guò)Callable和Future創(chuàng)建線(xiàn)程
創(chuàng)建線(xiàn)程的三種方式的對(duì)比:
(1)采用實(shí)現(xiàn) Runnable、Callable 接口的方式創(chuàng)見(jiàn)多線(xiàn)程時(shí),線(xiàn)程類(lèi)只是實(shí)現(xiàn)了 Runnable 接口或 Callable 接口,還可以繼承其他類(lèi)。
(2)使用繼承 Thread 類(lèi)的方式創(chuàng)建多線(xiàn)程時(shí),編寫(xiě)簡(jiǎn)單,如果需要訪(fǎng)問(wèn)當(dāng)前線(xiàn)程,則無(wú)需使用 Thread.currentThread() 方法,直接使用 this 即可獲得當(dāng)前線(xiàn)程。
3. 線(xiàn)程有哪幾種狀態(tài)?它們之間如何流轉(zhuǎn)的?

新建狀態(tài):
使用 new 關(guān)鍵字和 Thread 類(lèi)或其子類(lèi)建立一個(gè)線(xiàn)程對(duì)象后,該線(xiàn)程對(duì)象就處于新建狀態(tài)。它保持這個(gè)狀態(tài)直到程序 start() 這個(gè)線(xiàn)程。
就緒狀態(tài):
當(dāng)線(xiàn)程對(duì)象調(diào)用了start()方法之后,該線(xiàn)程就進(jìn)入就緒狀態(tài)。就緒狀態(tài)的線(xiàn)程處于就緒隊(duì)列中,要等待JVM里線(xiàn)程調(diào)度器的調(diào)度。
運(yùn)行狀態(tài):
如果就緒狀態(tài)的線(xiàn)程獲取 CPU 資源,就可以執(zhí)行 run(),此時(shí)線(xiàn)程便處于運(yùn)行狀態(tài)。處于運(yùn)行狀態(tài)的線(xiàn)程最為復(fù)雜,它可以變?yōu)樽枞麪顟B(tài)、就緒狀態(tài)和死亡狀態(tài)。
阻塞狀態(tài):
如果一個(gè)線(xiàn)程執(zhí)行了sleep(睡眠)、suspend(掛起)等方法,失去所占用資源之后,該線(xiàn)程就從運(yùn)行狀態(tài)進(jìn)入阻塞狀態(tài)。在睡眠時(shí)間已到或獲得設(shè)備資源后可以重新進(jìn)入就緒狀態(tài)??梢苑譃槿N:
等待阻塞:運(yùn)行狀態(tài)中的線(xiàn)程執(zhí)行 wait() 方法,使線(xiàn)程進(jìn)入到等待阻塞狀態(tài)。
同步阻塞:線(xiàn)程在獲取 synchronized 同步鎖失敗(因?yàn)橥芥i被其他線(xiàn)程占用)。
其他阻塞:通過(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 處理完畢,線(xiàn)程重新轉(zhuǎn)入就緒狀態(tài)。
死亡狀態(tài):
一個(gè)運(yùn)行狀態(tài)的線(xiàn)程完成任務(wù)或者其他終止條件發(fā)生時(shí),該線(xiàn)程就切換到終止?fàn)顟B(tài)。
4. 線(xiàn)程中的start()和run()方法有什么區(qū)別?
- start() 可以啟動(dòng)一個(gè)新線(xiàn)程,run()不能
- start()不能被重復(fù)調(diào)用,run()可以
- start()中的run代碼可以不執(zhí)行完就繼續(xù)執(zhí)行下面的代碼,即進(jìn)行了線(xiàn)程切換。直接調(diào)用run方法必須等待其代碼全部執(zhí)行完才能繼續(xù)執(zhí)行下面的代碼。
- start() 實(shí)現(xiàn)了多線(xiàn)程,run()沒(méi)有實(shí)現(xiàn)多線(xiàn)程。
5. 怎么終止一個(gè)線(xiàn)程?如何優(yōu)雅地終止線(xiàn)程?
- 使用退出標(biāo)志,使線(xiàn)程正常退出,也就是當(dāng)run方法完成后線(xiàn)程終止;
- 使用stop方法強(qiáng)行終止線(xiàn)程;
- 使用interrupt方法中斷線(xiàn)程。
6. ThreadLocal在多線(xiàn)程中扮演什么角色?
為了解決多線(xiàn)程中相同變量的訪(fǎng)問(wèn)沖突問(wèn)題。
ThreadLocal為每一個(gè)線(xiàn)程提供一個(gè)獨(dú)立的變量副本,從而隔離了多個(gè)線(xiàn) 程對(duì)訪(fǎng)問(wèn)數(shù)據(jù)的沖突。因?yàn)槊恳粋€(gè)線(xiàn)程都擁有自己的變量副本,從而也就沒(méi)有必要對(duì)該變量進(jìn)行同步了。ThreadLocal提供了線(xiàn)程安全的對(duì)象封裝,在編寫(xiě)多線(xiàn)程代碼時(shí),可以把不安全的變量封裝進(jìn)ThreadLocal。
線(xiàn)程并發(fā)時(shí),使用ThreadLocal在保證每個(gè)線(xiàn)程擁有自己的獨(dú)立對(duì)象,線(xiàn)程間互不影響。
“以空間換時(shí)間”的方式:訪(fǎng)問(wèn)并行化,對(duì)象獨(dú)享化。前者僅提供一份變量,讓不同的線(xiàn)程排隊(duì)訪(fǎng)問(wèn)。
7. 線(xiàn)程中的wait()和sleep()方法有什么區(qū)別?
sleep與wait都可以使線(xiàn)程等待,但sleep不會(huì)釋放資源而wait會(huì)釋放資源。
還有就是,wait方法只能在同步塊或者同步方法中執(zhí)行。
8. 多線(xiàn)程同步有哪幾種方法?
多線(xiàn)程并發(fā),當(dāng)多個(gè)線(xiàn)程同時(shí)操作一個(gè)可共享的資源變量時(shí)(如數(shù)據(jù)的增刪改查), 將會(huì)導(dǎo)致數(shù)據(jù)不準(zhǔn)確,相互之間產(chǎn)生沖突,因此加入同步鎖以避免在該線(xiàn)程沒(méi)有完成操作之前,被其他線(xiàn)程的調(diào)用, 從而保證了該變量的唯一性和準(zhǔn)確性。
8.1 同步方法
即有synchronized關(guān)鍵字修飾的方法。
由于java的每個(gè)對(duì)象都有一個(gè)內(nèi)置鎖,當(dāng)用此關(guān)鍵字修飾方法時(shí),
內(nèi)置鎖會(huì)保護(hù)整個(gè)方法。在調(diào)用該方法前,需要獲得內(nèi)置鎖,否則就處于阻塞狀態(tài)。
public synchronized void save()
{
...
}
注: synchronized關(guān)鍵字也可以修飾靜態(tài)方法,此時(shí)如果調(diào)用該靜態(tài)方法,將會(huì)鎖住整個(gè)類(lèi)。
8.2 同步代碼塊
即有synchronized關(guān)鍵字修飾的語(yǔ)句塊。
被該關(guān)鍵字修飾的語(yǔ)句塊會(huì)自動(dòng)被加上內(nèi)置鎖,從而實(shí)現(xiàn)同步。
8.3 使用特殊域變量(volatile)
a.volatile關(guān)鍵字為域變量的訪(fǎng)問(wèn)提供了一種免鎖機(jī)制;
b.使用volatile修飾域相當(dāng)于告訴虛擬機(jī)該域可能會(huì)被其他線(xiàn)程更新;
c.因此每次使用該域就要重新計(jì)算,而不是使用寄存器中的值;
d.volatile不會(huì)提供任何原子操作,它也不能用來(lái)修飾final類(lèi)型的變量。
8.4 使用重入鎖
8.5 使用局部變量(ThreadLocal)
9. 什么是死鎖?如何避免死鎖?
死鎖是指多個(gè)進(jìn)程循環(huán)等待它方占有的資源而無(wú)限期地僵持下去的局面。
使產(chǎn)生死鎖的四個(gè)必要條件不能同時(shí)具備;
- 安全序列
- 銀行家算法
10. 多線(xiàn)程之間如何進(jìn)行通信?
- 鎖機(jī)制:包括互斥鎖、條件變量、讀寫(xiě)鎖
互斥鎖提供了以排他方式防止數(shù)據(jù)結(jié)構(gòu)被并發(fā)修改的方法。
讀寫(xiě)鎖允許多個(gè)線(xiàn)程同時(shí)讀共享數(shù)據(jù),而對(duì)寫(xiě)操作是互斥的。
條件變量可以以原子的方式阻塞進(jìn)程,直到某個(gè)特定條件為真為止。對(duì)條件的測(cè)試是在互斥鎖的保護(hù)下進(jìn)行的。條件變量始終與互斥鎖一起使用。 - 信號(hào)量機(jī)制(Semaphore):包括無(wú)名線(xiàn)程信號(hào)量和命名線(xiàn)程信號(hào)量
- 信號(hào)機(jī)制(Signal):類(lèi)似進(jìn)程間的信號(hào)處理
線(xiàn)程間的通信目的主要是用于線(xiàn)程同步,所以線(xiàn)程沒(méi)有像進(jìn)程通信中的用于數(shù)據(jù)交換的通信機(jī)制。
11. 線(xiàn)程怎樣返回結(jié)果?如何獲取?
- 實(shí)現(xiàn)Callable接口,獲取一個(gè)Future的對(duì)象,在該對(duì)象上調(diào)用get就可以獲取返回結(jié)果;
- 通過(guò)回調(diào)函數(shù)返回?cái)?shù)據(jù)。
12. violatile關(guān)鍵字有什么用,和synchronized有什么區(qū)別?
它所修飾的變量不保留拷貝,直接訪(fǎng)問(wèn)主內(nèi)存中的。
在Java內(nèi)存模型中,有main memory,每個(gè)線(xiàn)程也有自己的memory (例如寄存器)。為了性能,一個(gè)線(xiàn)程會(huì)在自己的memory中保持要訪(fǎng)問(wèn)的變量的副本。這樣就會(huì)出現(xiàn)同一個(gè)變 量在某個(gè)瞬間,在一個(gè)線(xiàn)程的memory中的值可能與另外一個(gè)線(xiàn)程memory中的值,或者main memory中的值不一致的情況。 一個(gè)變量聲明為volatile,就意味著這個(gè)變量是隨時(shí)會(huì)被其他線(xiàn)程修改的,因此不能將它c(diǎn)ache在線(xiàn)程memory中。
區(qū)別:
- volatile是變量修飾符,而synchronized則作用于一段代碼或方法。
- volatile只是在線(xiàn)程內(nèi)存和“主”內(nèi)存間同步某個(gè)變量的值;而synchronized通過(guò)鎖定和解鎖某個(gè)監(jiān)視器同步所有變量的值。顯然synchronized要比volatile消耗更多資源。
13. 假如新建T1、T2、T3三個(gè)線(xiàn)程,如何保證它們按順序執(zhí)行?
可以用線(xiàn)程類(lèi)的join()方法在一個(gè)線(xiàn)程中啟動(dòng)另一個(gè)線(xiàn)程,另一個(gè)線(xiàn)程完成。
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.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("t2");
}
});
final Thread t3 = new Thread(new Runnable() {
@Override
public void run() {
try {
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("t3");
}
});
t1.start();
t2.start();
t3.start();
14. 怎么控制同一時(shí)間只有3個(gè)線(xiàn)程運(yùn)行?
Semaphore 控制并發(fā)訪(fǎng)問(wèn)線(xiàn)程數(shù)。
15. 為什么要使用線(xiàn)程池?
在Java中,如果每當(dāng)一個(gè)請(qǐng)求到達(dá)就創(chuàng)建一個(gè)新線(xiàn)程,開(kāi)銷(xiāo)是相當(dāng)大的。在實(shí)際使用中,每個(gè)請(qǐng)求創(chuàng)建新線(xiàn)程的服務(wù)器在創(chuàng)建和銷(xiāo)毀線(xiàn)程上花費(fèi)的時(shí)間和消耗的系統(tǒng)資源,甚至可能要比花在處理實(shí)際的用戶(hù)請(qǐng)求的時(shí)間和資源要多得多。除了創(chuàng)建和銷(xiāo)毀線(xiàn)程的開(kāi)銷(xiāo)之外,活動(dòng)的線(xiàn)程也需要消耗系統(tǒng)資源。如果在一個(gè)JVM里創(chuàng)建太多的線(xiàn)程,可能會(huì)導(dǎo)致系統(tǒng)由于過(guò)度消耗內(nèi)存或“切換過(guò)度”而導(dǎo)致系統(tǒng)資源不足。為了防止資源不足,服務(wù)器應(yīng)用程序需要一些辦法來(lái)限制任何給定時(shí)刻處理的請(qǐng)求數(shù)目,盡可能減少創(chuàng)建和銷(xiāo)毀線(xiàn)程的次數(shù),特別是一些資源耗費(fèi)比較大的線(xiàn)程的創(chuàng)建和銷(xiāo)毀,盡量利用已有對(duì)象來(lái)進(jìn)行服務(wù),這就是“池化資源”技術(shù)產(chǎn)生的原因。
線(xiàn)程池主要用來(lái)解決線(xiàn)程生命周期開(kāi)銷(xiāo)問(wèn)題和資源不足問(wèn)題。通過(guò)對(duì)多個(gè)任務(wù)重用線(xiàn)程,線(xiàn)程創(chuàng)建的開(kāi)銷(xiāo)就被分?jǐn)偟搅硕鄠€(gè)任務(wù)上了,而且由于在請(qǐng)求到達(dá)時(shí)線(xiàn)程已經(jīng)存在,所以消除了線(xiàn)程創(chuàng)建所帶來(lái)的延遲。這樣,就可以立即為請(qǐng)求服務(wù),使應(yīng)用程序響應(yīng)更快。另外,通過(guò)適當(dāng)?shù)卣{(diào)整線(xiàn)程池中的線(xiàn)程數(shù)目可以防止出現(xiàn)資源不足的情況。
16. 說(shuō)一說(shuō)常用的幾種線(xiàn)程池并講講其中的工作原理。
- newFixedThreadPool
創(chuàng)建一個(gè)指定工作線(xiàn)程數(shù)量的線(xiàn)程池。每當(dāng)提交一個(gè)任務(wù)就創(chuàng)建一個(gè)工作線(xiàn)程,如果工作線(xiàn)程數(shù)量達(dá)到線(xiàn)程池初始的最大數(shù),則將提交的任務(wù)存入到池隊(duì)列中。
FixedThreadPool是一個(gè)典型且優(yōu)秀的線(xiàn)程池,它具有線(xiàn)程池提高程序效率和節(jié)省創(chuàng)建線(xiàn)程時(shí)所耗的開(kāi)銷(xiāo)的優(yōu)點(diǎn)。但是,在線(xiàn)程池空閑時(shí),即線(xiàn)程池中沒(méi)有可運(yùn)行任務(wù)時(shí),它不會(huì)釋放工作線(xiàn)程,還會(huì)占用一定的系統(tǒng)資源。 - newCachedThreadPool
創(chuàng)建一個(gè)可緩存線(xiàn)程池,如果線(xiàn)程池長(zhǎng)度超過(guò)處理需要,可靈活回收空閑線(xiàn)程,若無(wú)可回收,則新建線(xiàn)程。
這種類(lèi)型的線(xiàn)程池特點(diǎn)是:
工作線(xiàn)程的創(chuàng)建數(shù)量幾乎沒(méi)有限制(其實(shí)也有限制的,數(shù)目為Interger. MAX_VALUE), 這樣可靈活的往線(xiàn)程池中添加線(xiàn)程。
如果長(zhǎng)時(shí)間沒(méi)有往線(xiàn)程池中提交任務(wù),即如果工作線(xiàn)程空閑了指定的時(shí)間(默認(rèn)為1分鐘),則該工作線(xiàn)程將自動(dòng)終止。終止后,如果你又提交了新的任務(wù),則線(xiàn)程池重新創(chuàng)建一個(gè)工作線(xiàn)程。
在使用CachedThreadPool時(shí),一定要注意控制任務(wù)的數(shù)量,否則,由于大量線(xiàn)程同時(shí)運(yùn)行,很有會(huì)造成系統(tǒng)癱瘓。 - newSingleThreadExecutor
創(chuàng)建一個(gè)單線(xiàn)程化的Executor,即只創(chuàng)建唯一的工作者線(xiàn)程來(lái)執(zhí)行任務(wù),它只會(huì)用唯一的工作線(xiàn)程來(lái)執(zhí)行任務(wù),保證所有任務(wù)按照指定順序(FIFO, LIFO, 優(yōu)先級(jí))執(zhí)行。如果這個(gè)線(xiàn)程異常結(jié)束,會(huì)有另一個(gè)取代它,保證順序執(zhí)行。單工作線(xiàn)程最大的特點(diǎn)是可保證順序地執(zhí)行各個(gè)任務(wù),并且在任意給定的時(shí)間不會(huì)有多個(gè)線(xiàn)程是活動(dòng)的。
4) newScheduleThreadPool
創(chuàng)建一個(gè)定長(zhǎng)的線(xiàn)程池,而且支持定時(shí)的以及周期性的任務(wù)執(zhí)行,支持定時(shí)及周期性任務(wù)執(zhí)行。
17. 線(xiàn)程池啟動(dòng)線(xiàn)程submit()和execute()有什么不同?
- execute提交的方式只能提交一個(gè)Runnable的對(duì)象,且該方法的返回值是void,也即是提交后如果線(xiàn)程運(yùn)行后,和主線(xiàn)程就脫離了關(guān)系了,當(dāng)然可以設(shè)置一些變量來(lái)獲取到線(xiàn)程的運(yùn)行結(jié)果。并且當(dāng)線(xiàn)程的執(zhí)行過(guò)程中拋出了異常通常來(lái)說(shuō)主線(xiàn)程也無(wú)法獲取到異常的信息的,只有通過(guò)ThreadFactory主動(dòng)設(shè)置線(xiàn)程的異常處理類(lèi)才能感知到提交的線(xiàn)程中的異常信息。
- submit提交的方式有如下三種情況
2.1) <T> Future<T> submit(Callable<T> task);
這種提交的方式是提交一個(gè)實(shí)現(xiàn)了Callable接口的對(duì)象,可以看到Callable接口和Runnable接口的定義很類(lèi)似,只不過(guò)Runnable接口中是一個(gè)沒(méi)有返回值的run方法,而Callable接口中是一個(gè)有返回值的call方法。
這種提交的方式會(huì)返回一個(gè)Future對(duì)象,這個(gè)Future對(duì)象代表這線(xiàn)程的執(zhí)行結(jié)果,當(dāng)主線(xiàn)程調(diào)用Future的get方法的時(shí)候會(huì)獲取到從線(xiàn)程中返回的結(jié)果數(shù)據(jù)。
如果在線(xiàn)程的執(zhí)行過(guò)程中發(fā)生了異常,get會(huì)獲取到異常的信息。
2.2) Future<?> submit(Runnable task);
也可以提交一個(gè)Runable接口的對(duì)象,這樣當(dāng)調(diào)用get方法的時(shí)候,如果線(xiàn)程執(zhí)行成功會(huì)直接返回null,如果線(xiàn)程執(zhí)行異常會(huì)返回異常的信息。
2.3) <T> Future<T> submit(Runnable task, T result);
當(dāng)線(xiàn)程正常結(jié)束的時(shí)候調(diào)用Future的get方法會(huì)返回result對(duì)象,當(dāng)線(xiàn)程拋出異常的時(shí)候會(huì)獲取到對(duì)應(yīng)的異常的信息。
18. 多線(xiàn)程并發(fā)控制中的倒計(jì)時(shí)器、循環(huán)柵欄是什么,有什么應(yīng)用場(chǎng)景?
- 倒計(jì)時(shí)器(CountDownLatch)
1.1) 實(shí)現(xiàn)最大的并行性:有時(shí)我們想同時(shí)啟動(dòng)多個(gè)線(xiàn)程,實(shí)現(xiàn)最大程度的并行性。例如,我們想測(cè)試一個(gè)單例類(lèi)。如果我們創(chuàng)建一個(gè)初始計(jì)數(shù)為1的CountDownLatch,并讓所有線(xiàn)程都在這個(gè)鎖上等待,那么我們可以很輕松地完成測(cè)試。我們只需調(diào)用 一次countDown()方法就可以讓所有的等待線(xiàn)程同時(shí)恢復(fù)執(zhí)行。
1.2) 開(kāi)始執(zhí)行前等待n個(gè)線(xiàn)程完成各自任務(wù):例如應(yīng)用程序啟動(dòng)類(lèi)要確保在處理用戶(hù)請(qǐng)求前,所有N個(gè)外部系統(tǒng)已經(jīng)啟動(dòng)和運(yùn)行了。
1.3) 死鎖檢測(cè):一個(gè)非常方便的使用場(chǎng)景是,你可以使用n個(gè)線(xiàn)程訪(fǎng)問(wèn)共享資源,在每次測(cè)試階段的線(xiàn)程數(shù)目是不同的,并嘗試產(chǎn)生死鎖。 - 循環(huán)柵欄(CyclicBarrier)
讓一組線(xiàn)程到達(dá)一個(gè)屏障(也可以叫同步點(diǎn))時(shí)被阻塞,直到最后一個(gè)線(xiàn)程到達(dá)屏障時(shí),屏障才會(huì)開(kāi)門(mén),所有被屏障攔截的線(xiàn)程才會(huì)繼續(xù)干活。
(1)CountDownLatch的計(jì)數(shù)器只能使用一次。而CyclicBarrier的計(jì)數(shù)器可以使用reset() 方法重置。所以CyclicBarrier能處理更為復(fù)雜的業(yè)務(wù)場(chǎng)景,比如如果計(jì)算發(fā)生錯(cuò)誤,可以重置計(jì)數(shù)器,并讓線(xiàn)程們重新執(zhí)行一次。
(2)CyclicBarrier還提供其他有用的方法,比如getNumberWaiting方法可以獲得CyclicBarrier阻塞的線(xiàn)程數(shù)量。isBroken方法用來(lái)知道阻塞的線(xiàn)程是否被中斷。比如以下代碼執(zhí)行完之后會(huì)返回true。
(3)CountDownLatch會(huì)阻塞主線(xiàn)程,CyclicBarrier不會(huì)阻塞主線(xiàn)程,只會(huì)阻塞子線(xiàn)程。
19. 什么是活鎖、饑餓、無(wú)鎖、死鎖?
- 死鎖:
多個(gè)線(xiàn)程相互占用對(duì)方的資源的鎖,而又相互等對(duì)方釋放鎖,此時(shí)若無(wú)外力干預(yù),這些線(xiàn)程則一直處理阻塞的假死狀態(tài),形成死鎖。 - 活鎖:
拿到資源卻又相互釋放不執(zhí)行。當(dāng)多線(xiàn)程中出現(xiàn)了相互謙讓?zhuān)贾鲃?dòng)將資源釋放給別的線(xiàn)程使用,這樣這個(gè)資源在多個(gè)線(xiàn)程之間跳動(dòng)而又得不到執(zhí)行。 - 饑餓:
3.1) 優(yōu)先級(jí)高的線(xiàn)程能夠插隊(duì)并優(yōu)先執(zhí)行,這樣如果優(yōu)先級(jí)高的線(xiàn)程一直搶占優(yōu)先級(jí)低線(xiàn)程的資源,導(dǎo)致低優(yōu)先級(jí)線(xiàn)程無(wú)法得到執(zhí)行。
3.2) 一個(gè)線(xiàn)程一直占著一個(gè)資源不放而導(dǎo)致其他線(xiàn)程得不到執(zhí)行,與死鎖不同的是饑餓在以后一段時(shí)間內(nèi)還是能夠得到執(zhí)行的,如那個(gè)占用資源的線(xiàn)程結(jié)束了并釋放了資源。 - 無(wú)鎖:
沒(méi)有對(duì)資源進(jìn)行鎖定,即所有的線(xiàn)程都能訪(fǎng)問(wèn)并修改同一個(gè)資源,但同時(shí)只有一個(gè)線(xiàn)程能修改成功。無(wú)鎖典型的特點(diǎn)就是一個(gè)修改操作在一個(gè)循環(huán)內(nèi)進(jìn)行,線(xiàn)程會(huì)不斷的嘗試修改共享資源,如果沒(méi)有沖突就修改成功并退出否則就會(huì)繼續(xù)下一次循環(huán)嘗試。所以,如果有多個(gè)線(xiàn)程修改同一個(gè)值必定會(huì)有一個(gè)線(xiàn)程能修改成功,而其他修改失敗的線(xiàn)程會(huì)不斷重試直到修改成功。
20. 什么是原子性、可見(jiàn)性、有序性?
- 原子性:
一個(gè)操作或者多個(gè)操作 要么全部執(zhí)行并且執(zhí)行的過(guò)程不會(huì)被任何因素打斷,要么就都不執(zhí)行。 - 可見(jiàn)性:
當(dāng)多個(gè)線(xiàn)程訪(fǎng)問(wèn)同一個(gè)變量時(shí),一個(gè)線(xiàn)程修改了這個(gè)變量的值,其他線(xiàn)程能夠立即看得到修改的值。
(volatile關(guān)鍵字、synchronized和Lock) - 有序性:
程序執(zhí)行的順序按照代碼的先后順序執(zhí)行。
(volatile關(guān)鍵字、synchronized和Lock)
21. 什么是守護(hù)線(xiàn)程?有什么用?
也稱(chēng)“服務(wù)線(xiàn)程”,在沒(méi)有用戶(hù)線(xiàn)程可服務(wù)時(shí)會(huì)自動(dòng)離開(kāi)。
為非后臺(tái)線(xiàn)程服務(wù)。
22. 怎么中斷一個(gè)線(xiàn)程?如何保證中斷業(yè)務(wù)不影響?
interrupt()
23. yield()方法有什么用?
- wait()和sleep()的關(guān)鍵的區(qū)別在于,wait()是用于線(xiàn)程間通信的,而sleep()是用于短時(shí)間暫停當(dāng)前線(xiàn)程。更加明顯的一個(gè)區(qū)別在于,當(dāng)一個(gè)線(xiàn)程調(diào)用wait()方法的時(shí)候,會(huì)釋放它鎖持有的對(duì)象的管程和鎖,但是調(diào)用sleep()方法的時(shí)候,不會(huì)釋放他所持有的管程。
-
yield和sleep的主要是,yield方法會(huì)臨時(shí)暫停當(dāng)前正在執(zhí)行的線(xiàn)程,來(lái)讓有同樣優(yōu)先級(jí)的正在等待的線(xiàn)程有機(jī)會(huì)執(zhí)行。如果沒(méi)有正在等待的線(xiàn)程,或者所有正在等待的線(xiàn)程的優(yōu)先級(jí)都比較低,那么該線(xiàn)程會(huì)繼續(xù)運(yùn)行。執(zhí)行了yield方法的線(xiàn)程什么時(shí)候會(huì)繼續(xù)運(yùn)行由線(xiàn)程調(diào)度器來(lái)決定,不同的廠商可能有不同的行為。yield方法不保證當(dāng)前的線(xiàn)程會(huì)暫停或者停止,但是可以保證當(dāng)前線(xiàn)程在調(diào)用yield方法時(shí)會(huì)放棄CPU。
thread.png
24. 什么是重入鎖,和Synchronized鎖有什么區(qū)別?
ReenTrantLock是一種自旋鎖,通過(guò)循環(huán)調(diào)用CAS操作來(lái)實(shí)現(xiàn)加鎖。它的性能比較好也是因?yàn)楸苊饬耸咕€(xiàn)程進(jìn)入內(nèi)核態(tài)的阻塞狀態(tài)。想盡辦法避免線(xiàn)程進(jìn)入內(nèi)核的阻塞狀態(tài)是我們?nèi)シ治龊屠斫怄i設(shè)計(jì)的關(guān)鍵鑰匙。
- ReenTrantLock可以指定是公平鎖還是非公平鎖。而synchronized只能是非公平鎖。所謂的公平鎖就是先等待的線(xiàn)程先獲得鎖。
- ReenTrantLock提供了一個(gè)Condition(條件)類(lèi),用來(lái)實(shí)現(xiàn)分組喚醒需要喚醒的線(xiàn)程們,而不是像synchronized要么隨機(jī)喚醒一個(gè)線(xiàn)程要么喚醒全部線(xiàn)程。
- ReenTrantLock提供了一種能夠中斷等待鎖的線(xiàn)程的機(jī)制,通過(guò)lock.lockInterruptibly()來(lái)實(shí)現(xiàn)這個(gè)機(jī)制。
25. Fork/Join框架是干什么的?
Fork/Join框架是一個(gè)比較特殊的線(xiàn)程池框架,專(zhuān)用于需要將一個(gè)任務(wù)不斷分解成多個(gè)子任務(wù)(分支),并將多個(gè)子任務(wù)的結(jié)果不斷進(jìn)行匯總得到最終結(jié)果(聚合)的并行計(jì)算框架。
Fork/Join框架適合能夠進(jìn)行拆分再合并的計(jì)算密集型(CPU密集型)任務(wù)。Fork/Join框架是一個(gè)并行框架,因此要求服務(wù)器擁有多CPU、多核,用以提高計(jì)算能力。
如果是單核、單CPU,不建議使用該框架,會(huì)帶來(lái)額外的性能開(kāi)銷(xiāo),反而比單線(xiàn)程的執(zhí)行效率低。當(dāng)然不是因?yàn)椴⑿械娜蝿?wù)會(huì)進(jìn)行頻繁的線(xiàn)程切換,因?yàn)镕ork/Join框架在進(jìn)行線(xiàn)程池初始化的時(shí)候默認(rèn)線(xiàn)程數(shù)量為Runtime.getRuntime().availableProcessors(),單CPU單核的情況下只會(huì)產(chǎn)生一個(gè)線(xiàn)程,并不會(huì)造成線(xiàn)程切換,而是會(huì)增加Fork/Join框架的一些隊(duì)列、池化的開(kāi)銷(xiāo)。
26. 如何給線(xiàn)程傳遞參數(shù)?
- 通過(guò)構(gòu)造方法傳遞數(shù)據(jù)
- 通過(guò)變量和方法傳遞數(shù)據(jù)
- 通過(guò)回調(diào)函數(shù)傳遞數(shù)據(jù)
27. 線(xiàn)程安全的和不安全的集合
- 線(xiàn)程安全的集合對(duì)象:
Vector、HashTable、StringBuffer - 非線(xiàn)程安全的集合對(duì)象:
ArrayList 、LinkedList、HashMap、HashSet、TreeMap、TreeSet、StringBulider
28. 什么是CAS算法?在多線(xiàn)程中有哪些應(yīng)用。
樂(lè)觀鎖( Optimistic Locking)其實(shí)是一種思想。相對(duì)悲觀鎖而言,樂(lè)觀鎖假設(shè)認(rèn)為數(shù)據(jù)一般情況下不會(huì)造成沖突,所以在數(shù)據(jù)進(jìn)行提交更新的時(shí)候,才會(huì)正式對(duì)數(shù)據(jù)的沖突與否進(jìn)行檢測(cè),如果發(fā)現(xiàn)沖突了,則讓返回用戶(hù)錯(cuò)誤的信息,讓用戶(hù)決定如何去做。
上面提到的樂(lè)觀鎖的概念中其實(shí)已經(jīng)闡述了他的具體實(shí)現(xiàn)細(xì)節(jié):主要就是兩個(gè)步驟:沖突檢測(cè)和數(shù)據(jù)更新。其實(shí)現(xiàn)方式有一種比較典型的就是Compare and Swap(CAS)。
CAS是項(xiàng)樂(lè)觀鎖技術(shù),當(dāng)多個(gè)線(xiàn)程嘗試使用CAS同時(shí)更新同一個(gè)變量時(shí),只有其中一個(gè)線(xiàn)程能更新變量的值,而其它線(xiàn)程都失敗,失敗的線(xiàn)程并不會(huì)被掛起,而是被告知這次競(jìng)爭(zhēng)中失敗,并可以再次嘗試。
