基礎(chǔ)知識點-多線程

1、什么是線程?

1)線程是輕量級的進程

2)線程沒有獨立的地址空間(內(nèi)存空間)

3)線程由進程創(chuàng)建(寄生在進程)

4)一個進程可擁有多個線程,同一進程的多個線程間可并發(fā)執(zhí)行

5)線程狀態(tài):新建(new)、就緒(Runnable)、運行(Running)、阻塞(Blocked)、死亡(Dead)

2、多線程的優(yōu)缺點?

優(yōu)點:

1)使程序響應(yīng)速度更快;

2)無處理任務(wù)時可將CPU時間讓給其它任務(wù);

3)可定期將CPU時間讓給其它任務(wù);

4)可隨時停止任務(wù);

5)可分別設(shè)置各任務(wù)優(yōu)先級以優(yōu)化性能

缺點:

1)線程需要占用內(nèi)存,線程越多占內(nèi)存越多;

2)多線程需協(xié)調(diào)和管理,需要CPU時間跟蹤線程;

3)線程間對共享資源的訪問會相互影響,必須解決競用共享資源的問題;

4)線程太多會導(dǎo)致控制復(fù)雜;

3、多線程的幾種實現(xiàn)方式?

1)繼承Thread類,重寫run方法;

2)實現(xiàn)Runnable接口,重寫run方法;

3)實現(xiàn)Callable接口通過FutureTask包裝器來創(chuàng)建Thread線程;

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

4、什么是線程安全?

若多個線程同時執(zhí)行一段程序結(jié)果和單線程結(jié)果一樣,且其他變量值也和預(yù)期一樣,就是線程安全。線程安全問題由全局變量及靜態(tài)變量引起。若每個線程對全局變量、靜態(tài)變量只有讀操作,這個全局變量是線程安全的;若多個線程同時執(zhí)行寫操作,需要考慮線程同步。存在競爭的線程不安全,不存在競爭的線程就是安全的。

5、Vector,SimpleDateFormat是線程安全類嗎?

Vector是線程安全的類,其在add()等操作上添加synchronized關(guān)鍵字實現(xiàn)同步,但并非絕對的線程安全,當(dāng)?shù)鷷r,如果在另一個線程執(zhí)行add(),remove()操作,仍有機率拋出異常ConcurrentModificationException。

SimpleDateFormat是線程不安全類,一般不要定義為static變量,如果定義為static必須加鎖,或使用DateUtils工具類。JAVA8下替代方式:用Instant代替Date,LocalDateTime代替Calendar,DateTimeFormatter代替SimpleDateFormat;

6、Java哪種原型不是線程安全的?

對基本類型的操作都是原子級的,除了long和double類型。JVM將32位作為原子操作,并非64位。當(dāng)線程把主存中的long/double類型的值讀到線程內(nèi)存時,可能是兩次32位值的寫操作,如果幾個線程同時操作,就可能會出現(xiàn)高低2個32位值出錯的情況發(fā)生。

在線程間共享long與double字段,必須在synchronized中操作,或聲明為volatile。

7、哪些集合類是線程安全的?

線程安全的集合對象:Vector、HashTable、StringBuffer

非線程安全的集合對象:ArrayList、LinkedList、HashMap、HashSet、TreeMap、TreeSet、StringBulider

8、多線程中的忙循環(huán)是什么?

就是用循環(huán)讓一個線程等待且不放棄CPU(而wait(), sleep()或yield()都放棄了CPU),目的是為了保留CPU緩存(在多核系統(tǒng)中,一個等待線程醒來時可能會在另一個內(nèi)核中運行,這樣會重建緩存,為避免重建緩存和減少等待重建時間)。

9、什么是線程局部變量Thread-Local Variable?

分別為每個線程存儲各自的屬性值,給每個線程使用。使用get()讀取值,set()設(shè)置值。如果線程是第一次訪問線程局部變量,線程局部變量可能還沒有為它存儲值,這時initialValue()會被調(diào)用,并返回當(dāng)前時間值。線程局部變量也提供remove(),用來刪除線程已存儲的值。

Java并發(fā)API包含了InheritableThreadLocal類,如果一個線程是從其他某個線程中創(chuàng)建的,這個類將提供繼承的值。如果一個線程A在線程局部變量已有值,當(dāng)它創(chuàng)建其它某個線程B時,線程B的局部變量將跟線程A一樣。你可以覆蓋ChileValue(),這個方法用來初始化子線程在線程局部變量中的值。它使用父線程在線程局部變量中的值作為傳入?yún)?shù)。

10、進程間如何通訊,線程間如何通訊?

進程與線程的區(qū)別:

1)線程產(chǎn)生的速度快,通訊快,切換快,因為處于同一地址空間。

2)線程的資源利用率好。

3)線程使用公共變量或內(nèi)存時需要同步機制,進程不用。

一、進程通訊:

管道(pipe):一種半雙工的通信方式,數(shù)據(jù)只能單向流動,且只能在具有親緣關(guān)系(通常指父子進程關(guān)系)的進程間使用。

有名管道(namedpipe):也是半雙工的通信方式,允許無親緣關(guān)系進程間的通信。

信號量(semophore):是一個計數(shù)器,可用來控制多個進程對共享資源的訪問。常作為一種鎖機制,防止某進程正在訪問共享資源時,其他進程也訪問該資源。主要作為進程間及同一進程內(nèi)不同線程間的同步手段。

消息隊列(messagequeue):消息鏈表,存放在內(nèi)核中并由消息隊列標(biāo)識符標(biāo)識??朔诵盘杺鬟f信息少、管道只能承載無格式字節(jié)流以及緩沖區(qū)大小受限等缺點。

信號(sinal):是一種比較復(fù)雜的通信方式,用于通知接收進程某個事件已經(jīng)發(fā)生。

共享內(nèi)存(shared memory):映射一段能被其他進程訪問的內(nèi)存,由一個進程創(chuàng)建,多個進程都可以訪問。是最快的IPC方式,與其他通信機制配合使用(如信號量),實現(xiàn)進程間的同步和通信。

套接字(socket):也是一種進程間通信機制,可用于不同設(shè)備間的進程通信。?

二、線程通信

鎖機制:包括互斥鎖、條件變量、讀寫鎖?

互斥鎖:提供了以排他方式防止數(shù)據(jù)結(jié)構(gòu)被并發(fā)修改的方法。

讀寫鎖:允許多個線程同時讀共享數(shù)據(jù),對寫操作互斥。

條件變量:以原子方式阻塞進程,直到某個特定條件為真為止。對條件的測試是在互斥鎖的保護下進行的。條件變量始終與互斥鎖一起使用。

信號量機制(Semaphore):包括無名線程信號量和命名線程信號量

信號機制(Signal):類似進程間的信號處理

11、什么是多線程環(huán)境下的偽共享(false sharing)?

在主存中緩存是以cache line為單元存儲的。cache line長度是2的指數(shù)倍,一般為32到256之間。大部分cache line長度為64 bytes。偽共享是指當(dāng)不同線程修改在同一個cache line中的多個不相關(guān)變量時,造成性能下降的問題。

12、同步和異步有何異同,在什么情況下分別使用他們?

同步交互:指發(fā)送一個請求需要等待返回,然后才能夠發(fā)送下一個請求,有個等待過程;

異步交互:指發(fā)送一個請求不需等待返回,隨時可以再發(fā)送下一個請求,即不需要等待。?

區(qū)別:一個需要等待,一個不需要等待,大部分情況下,優(yōu)先選擇異步交互方式。

同步示例:銀行的轉(zhuǎn)賬系統(tǒng),對數(shù)據(jù)庫的保存操作等;

13、ConcurrentHashMap和Hashtable區(qū)別?

都可用于多線程環(huán)境,ConcurrentHashMap僅鎖定map的某個部分,Hashtable會鎖定整個map。

14、ArrayBlockingQueue, CountDownLatch用法?

BlockingQueue:一種阻塞的FIFO queue,每個BlockingQueue都有一個容量。當(dāng)容量滿時,添加數(shù)據(jù)會阻塞,當(dāng)容量為空時,取元素會阻塞。BlockingQueue的兩個實現(xiàn)類:

ArrayBlockingQueue

1)由數(shù)組支持的有界阻塞隊列。

2)按FIFO(先進先出)排序元素。

3)一旦創(chuàng)建好這個數(shù)組,就不能再增加其容量。

4)向已滿隊列中放入元素會阻塞。

5)從空隊列中提取元素會阻塞。

LinkedBlockingQueue

1)基于已鏈接節(jié)點的、范圍任意的blocking queue的實現(xiàn)

2)按FIFO(先進先出)排序元素。

3)新元素插入到隊列尾部,隊列檢索會獲得隊列頭部的元素。吞吐量通常高于基于數(shù)組的隊列。

4)容量范圍可在構(gòu)造方法參數(shù)中指定,防止隊列過度擴展。

5)如果未指定容量,則它等于Integer.MAX_VALUE。除非插入節(jié)點會使隊列超出容量,否則每次插入后會動態(tài)創(chuàng)建鏈接節(jié)點。

6)是線程阻塞安全的

7)不接受null元素

8)實現(xiàn)了Collection和Iterator接口的所有可選方法

二者區(qū)別:

1)隊列中鎖的實現(xiàn)不同

ArrayBlockingQueue中的鎖是沒有分離的,生產(chǎn)和消費用同一個鎖;

LinkedBlockingQueue中的鎖是分離的,生產(chǎn)用putLock,消費是takeLock;

2)在生產(chǎn)或消費時操作不同

ArrayBlockingQueue在生產(chǎn)和消費時,直接將枚舉對象插入或移除;

LinkedBlockingQueue在生產(chǎn)和消費時,需要把枚舉對象轉(zhuǎn)換為Node<E>進行插入或移除,會影響性能;

3)隊列大小初始化方式不同

ArrayBlockingQueue必須指定隊列大??;

LinkedBlockingQueue可以不指定隊列大小,默認是Integer.MAX_VALUE;

CountDownLatch:同步輔助類,在完成一組正在其他線程中執(zhí)行的操作前,允許一個或多個線程一直等待。主要方法:

1)public CountDownLatch(count); //指定了計數(shù)的次數(shù)

2)public void countDown(); //當(dāng)前線程調(diào)用此方法則計數(shù)減一

3)public void await() ; //調(diào)用該方法會一直阻塞當(dāng)前線程,直到計時器的值為0

15、ConcurrentHashMap的并發(fā)度(Concurrency Level)是什么?

并發(fā)度:程序運行時能夠同時更新ConccurentHashMap且不產(chǎn)生鎖競爭的最大線程數(shù),實際上就是ConcurrentHashMap中的分段鎖個數(shù)(即Segment[]數(shù)組長度)。默認并發(fā)度為16,可以在構(gòu)造函數(shù)中設(shè)置并發(fā)度。當(dāng)用戶設(shè)置并發(fā)度時,ConcurrentHashMap會使用大于等于該值的最小2冪指數(shù)作為實際并發(fā)度(假如用戶設(shè)置并發(fā)度為17,實際并發(fā)度則為32)。運行時通過將key的高n位(n = 32 – segmentShift)和并發(fā)度減1(segmentMask)做位與運算定位到所在的Segment。segmentShift與segmentMask都是在構(gòu)造過程中根據(jù)concurrency level被相應(yīng)的計算出來。如果并發(fā)度設(shè)置過小,會帶來嚴重的鎖競爭問題;如果并發(fā)度設(shè)置過大,原本位于同一個Segment內(nèi)的訪問會擴散到不同的Segment中,CPU cache命中率會下降,從而引起程序性能下降。

16、CyclicBarrier和CountDownLatch有什么不同?各自的內(nèi)部原理和用法是什么?

CountDownLatch:一個或多個線程,等待另外N個線程完成某個事情后才能執(zhí)行。

CyclicBarrier:N個線程相互等待,任何一個線程完成,所有線程都必須等待。

CountDownLatch是計數(shù)器,線程完成一個就記一個,類似報數(shù), 只不過是遞減的。

CyclicBarrier像一個水閘,線程執(zhí)行像水流在水閘處都會堵住,等水滿(線程到齊)才開始泄流。

總結(jié):

CyclicBarrier就是一個柵欄,等待所有線程到達后再執(zhí)行操作。barrier在釋放等待線程后可重用。

CounDownLatch對于管理一組相關(guān)線程非常有用。

17、Semaphore的用法

一個線程同步輔助類,可以控制同時訪問資源的線程個數(shù),并提供了同步機制。例如,實現(xiàn)一個文件允許的并發(fā)訪問數(shù)。單個信號量的Semaphore對象可以實現(xiàn)互斥鎖功能,由一個線程獲得“鎖”,再由另一個線程釋放“鎖”,可應(yīng)用于死鎖恢復(fù)的場合。

Semaphore的主要方法:

void acquire():獲取一個許可,在提供許可前一直將線程阻塞,否則線程被中斷。

void release():釋放一個許可,將其返回給信號量。

int availablePermits():返回此信號量中當(dāng)前可用的許可數(shù)。

boolean hasQueuedThreads():查詢是否有線程正在等待獲取。

18、啟動一個線程是調(diào)用run()還是start()?start()和run()有什么區(qū)別?

調(diào)用start()可啟動線程,run()只是thread的一個普通方法,還是在主線程里執(zhí)行。

19、調(diào)用start()時會執(zhí)行run(),為什么不能直接調(diào)用run()?

run()只是類的一個普通方法,如果直接調(diào)用,程序中依然只有主線程這一個線程,其程序執(zhí)行路徑還是只有一條,還是要順序執(zhí)行,還是要等待run方法體執(zhí)行完畢后才可繼續(xù)執(zhí)行下面的代碼,這樣就沒有達到寫線程的目的。

20、sleep()和對象的wait()都可以讓線程暫停執(zhí)行,有什么區(qū)別?

sleep()是線程類的靜態(tài)方法,讓當(dāng)前線程暫停指定的時間,將CPU讓給其他線程,但對象的鎖依然保持,休眠時間結(jié)束后會自動恢復(fù)。

wait()是Object類的方法,調(diào)用對象的wait()導(dǎo)致當(dāng)前線程放棄對象的鎖(線程暫停執(zhí)行),進入對象的等待池(wait pool),只有調(diào)用對象的notify()或notifyAll()時才能喚醒等待池中的線程進入鎖定池(lock pool),線程重新獲得對象的鎖就可以進入就緒狀態(tài)。

21、yield()有什么作用?sleep()和yield()有什么區(qū)別?

1)sleep()給其他線程運行機會時不考慮線程優(yōu)先級;yield()只給相同或更高優(yōu)先級的線程運行機會;?

2)線程執(zhí)行sleep()后轉(zhuǎn)入阻塞(blocked)狀態(tài),執(zhí)行yield()轉(zhuǎn)入就緒(ready)狀態(tài);?

3)sleep()聲明拋出InterruptedException,yield()沒有聲明任何異常;?

4)sleep()比yield()具有更好的可移植性。

22、如何停止一個線程?

1)當(dāng)run()完成后線程終止。

2)使用interrupt()中斷線程。

23、stop()和suspend()為何不推薦使用?

stop()作為一種粗暴的線程終止行為,在線程終止前沒有對其做任何清除操作,具有不安全性。

suspend()具有死鎖傾向,調(diào)用suspend()時目標(biāo)線程會掛起。如果目標(biāo)線程掛起時在保護關(guān)鍵系統(tǒng)資源的監(jiān)視器上持有鎖,則在目標(biāo)線程重新開始前,其他線程都不能訪問該資源。對任何其他線程來說,如果想恢復(fù)目標(biāo)線程,同時又試圖使用任何一個鎖定的資源,就會造成死鎖。

24、如何在兩個線程間共享數(shù)據(jù)?

1)如果每個線程執(zhí)行的代碼相同,可使用同一個Runnable對象,例如賣票系統(tǒng)。?

2)如果每個線程執(zhí)行的代碼不同,需要用不同的Runnable對象,例如設(shè)計4個線程,其中兩個線程每次對j增加1,另外兩個線程每次對j減1,銀行存取款。

25、如何讓正在運行的線程暫停一段時間?

1)使用Sleep():并不會讓線程終止,一旦線程從休眠中喚醒,線程的狀態(tài)將會變?yōu)镽unnable,并且根據(jù)線程調(diào)度執(zhí)行。

2)使用wait():wait()里參數(shù)是暫停時間長度,以毫秒為單位。

26、什么是線程組,為什么Java不推薦使用?

線程組是由線程組成的管理線程的類,java.lang.ThreadGroup類。ThreadGroup類中的某些方法,可以對線程組中的線程產(chǎn)生作用。線程組中的線程可以修改組內(nèi)的其他線程,包括那些位于分層結(jié)構(gòu)最深處的。一個線程不能修改位于自己所在組或下屬組之外的任何線程。

線程組ThreadGroup對象中比較有用的方法是stop、resume、suspend等,這些方法會導(dǎo)致線程安全問題(主要是死鎖),已被廢棄。

線程組ThreadGroup不是線程安全的,在使用過程中獲取的信息并不是及時有效的,降低了使用價值。

27、你是如何調(diào)用wait()的?使用if塊還是while循環(huán)?為什么?

wait()應(yīng)該在循環(huán)調(diào)用,當(dāng)線程獲得CPU開始執(zhí)行時,其他條件可能還沒滿足,應(yīng)在處理前循環(huán)檢測條件是否滿足。

synchronized (obj) {

while (condition does not hold)

obj.wait(); // (Releases lock, and reacquires on wakeup)

... // Perform action appropriate to condition

}

28、有哪些不同的線程生命周期?

新建(new):當(dāng)創(chuàng)建Thread類的一個實例(對象)時,此線程進入新建狀態(tài)。

就緒(runnable):線程已啟動,在就緒隊列中排隊等候得到CPU資源。例如:t1.start();

運行(running):線程獲得CPU資源正在執(zhí)行任務(wù)run(),此時除非此線程自動放棄CPU資源或者有更高優(yōu)先級的線程進入,線程將一直運行到結(jié)束。

死亡(dead):當(dāng)線程執(zhí)行完或被其它線程殺死,進入死亡狀態(tài),這時線程不能再進入就緒狀態(tài)。

堵塞(blocked):由于某種原因?qū)е抡谶\行的線程讓出CPU并暫停自己的執(zhí)行,進入堵塞狀態(tài)。

睡眠:sleep(long t) 可使線程進入睡眠,一個睡眠的線程在指定的時間過去后可進入就緒狀態(tài)。

等待:調(diào)用wait(),調(diào)用motify()或notifyAll()回到就緒狀態(tài)。

被另一個線程所阻塞:調(diào)用suspend(),調(diào)用resume()恢復(fù)。

29、線程狀態(tài)BLOCKED和WAITING有什么區(qū)別?

線程處于BLOCKED狀態(tài)的場景:

1)當(dāng)前線程在等待一個monitor lock,比如等待執(zhí)行synchronized代碼塊或使用synchronized的方法。

2)在synchronized塊中循環(huán)調(diào)用Object.wait()

線程處于WAITING狀態(tài)的場景:

1)調(diào)用Object.wait(),但沒指定超時值。

2)調(diào)用Thread.join(),但沒指定超時值。

3)調(diào)用LockSupport對象的park()。

線程處于TIMED_WAITING狀態(tài)的場景:

1)調(diào)用Thread.sleep()。

2)調(diào)用Object.wait(),指定超時值。

3)調(diào)用Thread.join(),指定超時值。

4)調(diào)用LockSupport.parkNanos()。

5)調(diào)用LockSupport.parkUntil()。

30、畫一個線程的生命周期狀態(tài)圖

31、ThreadLocal用途是什么,原理是什么,用的時候要注意什么?

ThreadLocal使用場景有:用來解決數(shù)據(jù)庫連接、Session管理等。

ThreadLocal線程本地變量在每個線程中對該變量會創(chuàng)建一個副本,即每個線程內(nèi)都會有一個該變量,在線程內(nèi)部都可使用,線程間互不影響,不存在線程安全問題,也不會影響程序執(zhí)行性能。

1)通過ThreadLocal創(chuàng)建的副本存儲在每個線程自己的threadLocals中;

2)threadLocals的類型ThreadLocalMap的鍵值為ThreadLocal對象,每個線程中可有多個threadLocal變量,如longLocal和stringLocal;

3)在進行g(shù)et前,必須先set,否則會報空指針異常;要想在get前不需要調(diào)用set就能正常訪問,必須重寫initialValue()。

32、線程池是什么?為什么要使用它?

java.util.concurrent.Executors提供了一個java.util.concurrent.Executor接口的實現(xiàn)用于創(chuàng)建線程池。線程池作用就是限制系統(tǒng)中執(zhí)行線程的數(shù)量。

為什么要用線程池:

1)減少了創(chuàng)建和銷毀線程的次數(shù),每個工作線程都可以被重復(fù)利用,可執(zhí)行多個任務(wù)。

2)根據(jù)系統(tǒng)的承受能力,調(diào)整線程池中工作線線程數(shù)目,防止因為消耗過多內(nèi)存,導(dǎo)致服務(wù)器宕機(每個線程需要大約1MB內(nèi)存,線程越多消耗內(nèi)存越大,最后死機)。

33、如何創(chuàng)建一個Java線程池?

1)newSingleThreadExecutor-單線程的線程池

線程池只有一個線程在工作,相當(dāng)于單線程串行執(zhí)行所有任務(wù)。如果這個唯一的線程因異常結(jié)束,會有一個新的線程來替代它。此線程池保證所有任務(wù)的執(zhí)行順序按任務(wù)的提交順序執(zhí)行。

2)newFixedThreadPool-固定大小的線程池

每次提交一個任務(wù)就創(chuàng)建一個線程,直到線程達到線程池的最大值。線程池的大小一旦達到最大值就會保持不變,如果某個線程因異常結(jié)束,那么線程池會補充一個新線程。

3)newCachedThreadPool-可緩存的線程池

如果線程池的大小超過了處理任務(wù)所需要的線程,就會回收部分空閑(60秒不執(zhí)行任務(wù))線程,當(dāng)任務(wù)數(shù)增加時,線程池又可以添加新線程來處理任務(wù)。此線程池不會對線程池大小做限制,線程池大小依賴于JVM能夠創(chuàng)建的最大線程大小。

4)newScheduledThreadPool-大小無限的線程池

此線程池支持定時及周期性執(zhí)行任務(wù)的需求。

34、ThreadPool用法與優(yōu)勢?

通過ThreadPoolExecutor來創(chuàng)建一個線程池。

new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, milliseconds,runnableTaskQueue, handler);

線程池的優(yōu)點:

1)降低資源消耗。通過重復(fù)利用已創(chuàng)建的線程降低線程創(chuàng)建和銷毀造成的消耗。

2)提高響應(yīng)速度。當(dāng)任務(wù)到達時,任務(wù)不需等到線程創(chuàng)建就能立即執(zhí)行。

3)提高線程的可管理性。線程是稀缺資源,如果無限制的創(chuàng)建,不僅會消耗系統(tǒng)資源,還會降低系統(tǒng)穩(wěn)定性,使用線程池可進行統(tǒng)一分配、調(diào)優(yōu)和監(jiān)控。

35、提交任務(wù)時,線程池隊列已滿時會發(fā)會生什么?

ThreadPoolExecutors.submit()會拋出RejectedExecutionException異常;

36、線程池的實現(xiàn)策略

RejectedExecutionHandler:飽和策略

當(dāng)隊列和線程池都滿了,說明線程池處于飽和狀態(tài),必須對新提交的任務(wù)采用一種特殊的策略進行處理。這個策略默認配置是AbortPolicy,表示無法處理新任務(wù)而拋出異常。

JAVA提供了4種策略:

1)AbortPolicy:直接拋出異常

2)CallerRunsPolicy:只用調(diào)用所在的線程運行任務(wù)

3)DiscardOldestPolicy:丟棄隊列里最近的一個任務(wù),并執(zhí)行當(dāng)前任務(wù)。

4)DiscardPolicy:不處理,丟棄掉。

37、線程池的關(guān)閉方式有幾種,各自的區(qū)別是什么?

對ExecutorService關(guān)閉方式有兩種:一種是調(diào)用shutdown(),另一種是調(diào)用shutdownNow()。

shutdown:

1)調(diào)用后不允許繼續(xù)往線程池內(nèi)添加線程;

2)線程池變?yōu)镾HUTDOWN狀態(tài);

3)所有在調(diào)用shutdown()前提交到ExecutorSrvice的任務(wù)都會執(zhí)行;

4)一旦所有線程結(jié)束執(zhí)行當(dāng)前任務(wù),ExecutorService才會真正關(guān)閉。

shutdownNow():

1)返回尚未執(zhí)行的任務(wù)列表;

2)線程池變?yōu)镾TOP狀態(tài);

3)阻止所有正在等待啟動的任務(wù), 并停止當(dāng)前正在執(zhí)行的任務(wù);

38、線程池中submit()和execute()有什么區(qū)別?

1)接收參數(shù)不一樣

2)submit有返回值,execute沒有

3)submit方便Exception處理

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

Windows中Java線程調(diào)度:

Java線程一對一地綁定到Win32線程上,Win32線程也是一對一綁定到內(nèi)核級線程上。JVM通過將Java線程的優(yōu)先級映射到Win32線程的優(yōu)先級上,從而影響系統(tǒng)的線程調(diào)度決策。

Windows內(nèi)核使用了32級優(yōu)先權(quán)模式來決定線程的調(diào)度順序。優(yōu)先權(quán)分為兩類:可變類優(yōu)先權(quán)(1-15級)、不可變類優(yōu)先權(quán)(16-31級)。調(diào)度程序為每一個優(yōu)先級建一個調(diào)度隊列,從高優(yōu)先級到低優(yōu)先級隊列逐個查找,直到找到一個可運行的線程。

Windows采用基于優(yōu)先級的、搶占的線程調(diào)度算法。調(diào)度程序保證總是讓具有最高優(yōu)先級的線程運行。一個線程僅在如下四種情況下才會放棄CPU:被更高優(yōu)先級的線程搶占、結(jié)束、時間片到、執(zhí)行導(dǎo)致阻塞的系統(tǒng)調(diào)用。

當(dāng)線程的時間片用完后,降低其優(yōu)先級;當(dāng)線程從阻塞變?yōu)榫途w時,增加線程的優(yōu)先級;當(dāng)線程長時間沒有機會運行時,系統(tǒng)也會提升線程優(yōu)先級。Windows區(qū)分前臺和后臺進程,前臺進程往往獲得更長的時間片。以上措施體現(xiàn)了Windows基于動態(tài)優(yōu)先級、分時和搶占的CPU調(diào)度策略。

Linux中Java線程調(diào)度:

在Linux上Java線程一對一映射到內(nèi)核級線程上。Linux中不區(qū)分進程和線程,同一個進程中的線程可看作是共享程度較高的一組進程。Linux也是通過優(yōu)先級來實現(xiàn)CPU分配,應(yīng)用程序可以通過調(diào)整nice值(謙讓值)來設(shè)置進程的優(yōu)先級。nice值反映了線程的謙讓程度,該值越高說明線程越有意愿把CPU讓給別的線程。JVM需要實現(xiàn)Java線程的優(yōu)先級到nice的映射。linux調(diào)度器實現(xiàn)了一個搶占的、基于優(yōu)先級的調(diào)度算法,支持兩種類型的進程調(diào)度:實時進程的優(yōu)先級范圍為[0,99],普通進程的優(yōu)先級范圍為[100,140]。

進程的優(yōu)先權(quán)越高,所獲得的時間片越大。每個就緒進程都有一個時間片,內(nèi)核將就緒進程分為活動的(active)和過期的(expired)兩類:只要進程的時間片沒有耗盡,就一直有資格運行,稱為活動的;當(dāng)進程的時間片耗盡后,就沒有資格運行了,稱為過期的。調(diào)度程序總是在活動的進程中選擇優(yōu)先級最高的進程執(zhí)行,直到所有的活動進程都耗盡了時間片。當(dāng)所有的活動進程都變成過期的之后,調(diào)度程序再將所有過期的進程置為活動的,并為他們分配相應(yīng)的時間片,重新進行新一輪的調(diào)度。

40、什么是多線程中的上下文切換?

CPU通過時間片段的算法來循環(huán)執(zhí)行線程任務(wù),循環(huán)執(zhí)行即每個線程在允許運行的時間后切換,這種循環(huán)切換使各個程序從表面看是同時進行的。切換時會保存之前的線程任務(wù)狀態(tài),當(dāng)切換到該線程任務(wù)時,會重新加載該線程的任務(wù)狀態(tài)。這個從保存到加載的過程稱為上下文切換。

若當(dāng)前線程還在運行而時間片結(jié)束后,CPU將被剝奪并分配給另一個線程。

若線程在時間片結(jié)束前阻塞或結(jié)束,CPU進行線程切換,不會造成CPU資源浪費。

41、你對線程優(yōu)先級的理解是什么?

每個線程都有優(yōu)先級,高優(yōu)先級的線程在運行時具有優(yōu)先權(quán),這依賴于線程調(diào)度的實現(xiàn),這個實現(xiàn)和操作系統(tǒng)相關(guān)(OSdependent)??梢远x線程的優(yōu)先級,但并不能保證高優(yōu)先級的線程會在低優(yōu)先級的線程前執(zhí)行。線程優(yōu)先級是一個int變量(從1-10,1最低,10最高)。

42、什么是線程調(diào)度器 (Thread Scheduler) 和時間分片 (Time Slicing)?

線程調(diào)度器:是一個操作系統(tǒng)服務(wù),負責(zé)為Runnable狀態(tài)的線程分配CPU時間。一旦創(chuàng)建一個線程并啟動它,它的執(zhí)行便依賴于線程調(diào)度器的實現(xiàn)。

時間分片:指將可用的CPU時間分配給可用的Runnable線程的過程。分配CPU時間可以基于線程優(yōu)先級或線程等待的時間。線程調(diào)度并不受JVM控制,由應(yīng)用程序來控制它更好。

43、請說出你所知的線程同步的方法?

wait():使一個線程處于等待狀態(tài),并釋放所持有的對象lock。

sleep():使一個正在運行的線程處于睡眠狀態(tài),是一個靜態(tài)方法,調(diào)用此方法要捕捉InterruptedException異常。

notify():喚醒一個處于等待狀態(tài)的線程,注意在調(diào)用此方法時,并不能確切的喚醒某一個等待狀態(tài)的線程,而是由JVM確定喚醒哪個線程,且不是按優(yōu)先級。

notifyAll():喚醒所有處入等待狀態(tài)的線程,并不是給所有喚醒線程一個對象的鎖,而是讓它們競爭。

44、synchronized的原理是什么?

synchronized底層是通過一個monitor對象來完成,wait/notify等方法也依賴于monitor對象,這就是為什么只有在同步塊或方法中才能調(diào)用wait/notify等方法,否則會拋出java.lang.IllegalMonitorStateException異常的原因。

45、synchronized和ReentrantLock有什么不同?

1)Synchronized是java關(guān)鍵字,是原生語法層面的互斥,需要jvm實現(xiàn)。ReentrantLock是JDK 1.5之后提供的API層面的互斥鎖,需要lock()和unlock()方法配合try/finally語句塊來完成。

2)Synchronized經(jīng)過編譯,會在同步塊的前后分別形成monitorenter和monitorexit兩個字節(jié)碼指令。在執(zhí)行monitorenter指令時,首先嘗試獲取對象鎖。如果這個對象沒被鎖定,或當(dāng)前線程已擁有了那個對象鎖,把鎖計算器加1,在執(zhí)行monitorexit指令時會將鎖計算器就減1,當(dāng)計算器為0時,鎖就被釋放。如果獲取對象鎖失敗,當(dāng)前線程就要阻塞,直到對象鎖被另一個線程釋放。

ReentrantLock是java.util.concurrent包下的一套互斥鎖,ReentrantLock類提供了一些高級功能,主要有以下3項:

1.等待可中斷,持有鎖的線程長期不釋放時,正在等待的線程可以放棄等待,避免出現(xiàn)死鎖。

2.公平鎖,多個線程等待同一個鎖時,必須按申請鎖的時間順序獲得鎖,Synchronized是非公平鎖,ReentrantLock默認構(gòu)造函數(shù)是創(chuàng)建的非公平鎖,可通過參數(shù)設(shè)為公平鎖,但公平鎖性能不高。

3.鎖綁定多個條件,一個ReentrantLock對象可同時綁定多個對象。

46、什么場景下可以使用volatile替換synchronized?

Synchronized修飾范圍為:類、方法、代碼塊、靜態(tài)方法,不能修飾變量,不能使變量共享。

volatile使用場景

1)volatile可以修飾變量,共享變量。

2)保障共享變量對所有線程的可見性。保證了不同線程對這個變量進行操作時的可見性,即一個線程修改了某個變量值,新值對其他線程是立即可見的。

Synchronized與volatile的區(qū)別:

1)volatile字段沒有涉及到鎖的操作。

2)volatile聲明的是變量,修飾的是變量。

47、有T1,T2,T3三個線程,怎么確保它們按順序執(zhí)行?怎樣保證T2在T1執(zhí)行完后執(zhí)行,T3在T2執(zhí)行完后執(zhí)行?

要保證T1、T2、T3三個線程順序執(zhí)行,可以利用Thread.join()。

Thread.join()主要作用是同步,可使線程間的并行執(zhí)行變?yōu)榇袌?zhí)行。當(dāng)調(diào)用某個線程的這個方法時,會掛起調(diào)用線程,直到被調(diào)用線程結(jié)束執(zhí)行,調(diào)用線程才會繼續(xù)執(zhí)行。

48、同步塊內(nèi)的線程拋出異常會發(fā)生什么?

無論同步塊是正常還是異常退出,里面的線程都會釋放鎖,該功能可在finally block里釋放鎖實現(xiàn)。

49、當(dāng)一個線程進入一個對象的synchronized方法A之后,其它線程是否可進入此對象的synchronized方法B?

不能,其他線程只能訪問該對象的非同步方法,同步方法則不能進入;synchronized修飾符要求執(zhí)行方法時要獲得對象鎖,如果已經(jīng)進入A方法,說明對象鎖已經(jīng)被取到。

50、使用synchronized修飾靜態(tài)方法和非靜態(tài)方法有什么區(qū)別?

1) synchronized static是某個類的范圍,synchronized static cSync{}防止多個線程同時訪問這個類中的synchronized static方法,可以對類的所有對象實例起作用。

2)synchronized是某實例的范圍,synchronized isSync(){}防止多個線程同時訪問這個實例中的synchronized方法。

51、如何從給定集合里創(chuàng)建一個synchronized的集合?

使用Collections.synchronizedCollection(Collection c)根據(jù)指定集合來獲取一個synchronized集合。

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

Lock與synchronized都能對多線程靜態(tài)資源的修改做同步(互斥)控制;

Lock優(yōu)勢:

1)操作方面(鎖控制):Lock可手動控制加鎖與釋放鎖,synchronized自動釋放鎖。

2)性能方面:使用Lock接口的實現(xiàn)類ReadWriteLock子類ReentrantReadWriteLock的對象rwl,在多線程同時對靜態(tài)資源進行修改時,可以使用rwl.readLock().lock()或rwl.writeLock().lock()對靜態(tài)資源做并發(fā)修改控制。讀寫鎖可以實現(xiàn)讀寫互斥,讀讀不互斥,這是synchroized實現(xiàn)不了的,synchroized對讀讀也互斥。

3)線程通信方面:Lock對應(yīng)的實現(xiàn)類對象lock通過lock.newCondition()可以實例化Condition對象condition,condition通過condition.await()實現(xiàn)synchronized + t.wait()的效果,t代表線程,通過condition.signal()或condition.signalAll()實現(xiàn)線程喚醒,且lock可以為讀寫線程創(chuàng)建兩種不同操作(read or write)類型的Condition對象,使得線程間通信要比傳統(tǒng)的wait()、notifiy()進行線程通信的效率高。使得加鎖、釋放鎖的操作更具選擇性,精準(zhǔn)性。

53、Lock與Synchronized的區(qū)別?Lock接口比synchronized塊的優(yōu)勢是什么?

1)Lock是一個接口,synchronized是關(guān)鍵字,synchronized是在JVM層面上實現(xiàn)的,可以通過一些監(jiān)控工具監(jiān)控synchronized的鎖定,在代碼執(zhí)行時出現(xiàn)異常,JVM會自動釋放鎖,Lock則不行,lock是通過代碼實現(xiàn)的,要保證鎖一定會被釋放,就必須將unLock()放到finally{}中;

2)synchronized發(fā)生異常時,會自動釋放線程占有的鎖,不會導(dǎo)致死鎖;Lock在異常時,如果沒有主動通過unLock()去釋放鎖,則很可能造成死鎖現(xiàn)象,使用Lock時需要在finally塊中釋放鎖;

3)Lock可以讓等待鎖的線程響應(yīng)中斷,線程可以中斷去干別的事務(wù),而synchronized不行,等待的線程會一直等待下去,不能響應(yīng)中斷;

4)通過Lock可以知道有沒有成功獲取鎖,synchronized無法辦到。

5)Lock可以提高多個線程讀操作的效率。當(dāng)競爭資源非常激烈時,Lock性能遠優(yōu)于synchronized。

54、ReadWriteLock是什么?

讀寫鎖是用來提升并發(fā)程序性能的鎖分離技術(shù)。一個ReadWriteLock維護一對關(guān)聯(lián)的鎖,一個用于只讀一個用于寫。在沒有寫線程的情況下,一個讀鎖可能會同時被多個讀線程持有。寫鎖是獨占的,可以使用ReentrantReadWriteLock來實現(xiàn)這個規(guī)則,它最多支持65535個寫鎖和65535個讀鎖。

55、鎖機制有什么用?

有些業(yè)務(wù)邏輯在執(zhí)行過程中要求對數(shù)據(jù)進行排他性訪問,需要通過一些機制保證在此過程中數(shù)據(jù)被鎖住不會被外界修改,這就是所謂的鎖機制。

56、什么是樂觀鎖(Optimistic Locking)?如何實現(xiàn)樂觀鎖?如何避免ABA問題?

Hibernate支持悲觀鎖和樂觀鎖兩種鎖機制。

悲觀鎖:悲觀的認為在數(shù)據(jù)處理過程中極有可能存在修改數(shù)據(jù)的并發(fā)事務(wù),并將處理的數(shù)據(jù)鎖定。必須依賴數(shù)據(jù)庫本身的鎖機制才能真正保證數(shù)據(jù)訪問的排他性。

樂觀鎖:對并發(fā)事務(wù)持樂觀態(tài)度,通過寬松的鎖機制來解決由于悲觀鎖排他性的數(shù)據(jù)訪問對系統(tǒng)性能造成的嚴重影響。最常見的樂觀鎖是通過數(shù)據(jù)版本標(biāo)識來實現(xiàn)的,讀取數(shù)據(jù)時獲得數(shù)據(jù)的版本號,更新數(shù)據(jù)時將此版本號+1,然后和數(shù)據(jù)庫表對應(yīng)記錄的當(dāng)前版本號進行比較,如果提交的數(shù)據(jù)版本號大于數(shù)據(jù)庫中此記錄的當(dāng)前版本號則更新,否則認為是過期數(shù)據(jù)無法更新。

Hibernate中通過Session的get()和load()方法從數(shù)據(jù)庫加載對象時可以通過參數(shù)指定使用悲觀鎖;而樂觀鎖可以通過給實體類加整型的版本字段再通過XML或@Version注解進行配置。

CAS是樂觀鎖技術(shù),當(dāng)多個線程使用CAS同時更新一個變量時,只有其中一個線程能更新變量值,其它都失敗,失敗的線程并不會被掛起,而是被告知這次競爭失敗,并可再次嘗試。

CAS操作包含三個操作數(shù):內(nèi)存位置(V)、預(yù)期原值(A)和新值(B)。如果內(nèi)存位置值與預(yù)期原值匹配,那么處理器會自動將該位置值更新為新值,否則不操作。無論哪種情況,它都會在CAS指令前返回該位置的值。CAS有效說明了“我認為位置V應(yīng)該包含值A(chǔ);如果包含,則將B放到這個位置;否則不更改,然后告訴我這個位置現(xiàn)在的值即可。”這和樂觀鎖的沖突檢查+數(shù)據(jù)更新原理一樣。

CAS會導(dǎo)致“ABA問題”。

CAS算法實現(xiàn)的重要前提是需要取出內(nèi)存中某時刻的數(shù)據(jù),在下一時刻比較并替換,在這個時間差內(nèi)可能會導(dǎo)致數(shù)據(jù)變化。比如線程1從內(nèi)存位置V中取出A,這時線程2也從內(nèi)存中取出A,并進行了一些操作變成了B,然后又將位置V的數(shù)據(jù)變成A,這時線程1進行CAS操作發(fā)現(xiàn)內(nèi)存中仍是A,然后線程1操作成功。盡管線程1的CAS操作成功,但不代表這個過程沒有問題。

通過對數(shù)據(jù)加版本號方式來解決ABA問題,樂觀鎖每次執(zhí)行數(shù)據(jù)修改時,都會帶上一個版本號,版本號和數(shù)據(jù)版本號一致就可以執(zhí)行修改并對版本號執(zhí)行+1操作,否則失敗。

57、解釋以下名詞:重排序,自旋鎖,偏向鎖,輕量級鎖,可重入鎖,公平鎖,非公平鎖,樂觀鎖,悲觀鎖?

重排序:通常是編譯器或運行時環(huán)境為了優(yōu)化程序性能對指令進行重新排序執(zhí)行的一種手段。重排序分為兩類:編譯器重排序和運行期重排序,分別對應(yīng)編譯時和運行時環(huán)境。

公平鎖/非公平鎖

公平鎖:是指多個線程按照申請鎖的時間順序來獲取鎖。

非公平鎖:是指多個線程獲取鎖的順序并不是按照申請鎖的順序,后申請的線程可能比先申請的優(yōu)先獲取鎖??赡軙斐蓛?yōu)先級反轉(zhuǎn)或饑餓現(xiàn)象。

ReentrantLock通過構(gòu)造函數(shù)指定該鎖是否公平鎖,默認是非公平鎖(優(yōu)點:吞吐量比公平鎖大)。

Synchronized也是非公平鎖,不像ReentrantLock通過AQS來實現(xiàn)線程調(diào)度,無法使其變成公平鎖。

可重入鎖/不可重入鎖

可重入鎖:可重復(fù)可遞歸調(diào)用的鎖,在外層使用鎖之后,在內(nèi)層仍可使用,且不發(fā)生死鎖(前提是同一個對象或class)。ReentrantLock和synchronized都是可重入鎖。

不可重入鎖:與可重入鎖相反,不可遞歸調(diào)用,遞歸調(diào)用就發(fā)生死鎖。

獨享鎖/共享鎖

獨享鎖:該鎖每次只能被一個線程所持有。Synchronized是獨享鎖。

共享鎖:該鎖可被多個線程共有,典型的就是ReentrantReadWriteLock里的讀鎖,讀鎖可被共享,寫鎖每次只能被獨占。讀鎖的共享可保證并發(fā)讀非常高效,但讀寫和寫寫,寫讀都是互斥的。

獨享鎖與共享鎖也是通過AQS來實現(xiàn)的。

互斥鎖/讀寫鎖

互斥鎖:在訪問共享資源前加鎖,訪問后解鎖。 加鎖后,任何試圖再次加鎖的線程會被阻塞,直到當(dāng)前線程解鎖。如果解鎖時有一個以上的線程阻塞,那么所有該鎖上的線程都被變成就緒狀態(tài), 第一個變?yōu)榫途w狀態(tài)的線程又執(zhí)行加鎖操作,其他線程又會進入等待。 在這種方式下,只有一個線程能訪問被互斥鎖保護的資源。

讀寫鎖:既是互斥鎖,又是共享鎖,讀共享,寫互斥(排它鎖)。具體實現(xiàn)是ReadWriteLock;

讀寫鎖有三種狀態(tài):讀加鎖狀態(tài)、寫加鎖狀態(tài)和不加鎖狀態(tài)。

樂觀鎖/悲觀鎖

悲觀鎖:總是假設(shè)最壞的情況,每次取數(shù)據(jù)時都認為別人會修改,所以每次取數(shù)據(jù)時都會上鎖,別人想拿這個數(shù)據(jù)就會阻塞直到它拿到鎖(共享資源每次只給一個線程使用,其它線程阻塞,用完后再把資源轉(zhuǎn)讓給其它線程)。傳統(tǒng)的關(guān)系型數(shù)據(jù)庫里就用到了很多這種鎖機制,比如行鎖,表鎖,讀鎖,寫鎖等,都是在操作前先上鎖。synchronized和ReentrantLock等獨占鎖就是悲觀鎖思想的實現(xiàn)。

樂觀鎖:總是假設(shè)最好的情況,每次取數(shù)據(jù)時都認為別人不會修改,所以不會上鎖,在更新時會判斷一下在此期間別人有沒有更新這個數(shù)據(jù),可以使用版本號機制和CAS算法實現(xiàn)。適用于多讀的應(yīng)用類型,可以提高吞吐量,像數(shù)據(jù)庫提供的類似于write_condition機制。java.util.concurrent.atomic包下的原子變量類就是使用了樂觀鎖的一種實現(xiàn)方式CAS實現(xiàn)的。

偏向鎖/輕量級鎖/重量級鎖

鎖的狀態(tài)分為四級:無鎖狀態(tài)、偏向鎖狀態(tài)、輕量級鎖狀態(tài)、重量級鎖狀態(tài);

四種狀態(tài)會隨著競爭逐漸升級,是不可逆的過程,即不可降級。這四種狀態(tài)都不是Java中的鎖,而是Jvm為提高鎖的獲取與釋放效率而做的優(yōu)化(使用synchronized時)。

偏向鎖:一段同步代碼一直被一個線程所訪問,該線程會自動獲取鎖,降低獲取鎖的代價。

輕量級鎖:當(dāng)鎖是偏向鎖時,被另一個線程所訪問,偏向鎖就會升級為輕量級鎖,其他線程會通過自旋的形式嘗試獲取鎖,不會阻塞,提高性能。

重量級鎖:當(dāng)鎖為輕量級鎖時,另一個線程雖然是自旋,但自旋不會一直持續(xù)下去,當(dāng)自旋一定次數(shù)時,還沒獲取到鎖,就會進入阻塞,該鎖膨脹為重量級鎖。重量級鎖會讓其他申請的線程進入阻塞,性能降低。

自旋鎖(spinlock):當(dāng)一個線程在獲取鎖時,如果鎖已被其它線程獲取,該線程將循環(huán)等待,然后不斷的判斷鎖是否能夠被獲取,直到獲取鎖才會退出循環(huán)。

自旋鎖:線程獲取鎖時,如果鎖被其他線程持有,則當(dāng)前線程將循環(huán)等待,直到獲取到鎖。

自旋鎖等待期間,線程的狀態(tài)不會改變,線程一直是用戶態(tài)并且是活動的(active)。

自旋鎖如果持有鎖的時間太長,則會導(dǎo)致其它等待獲取鎖的線程耗盡CPU。

自旋鎖無法保證公平性、可重入性。

基于自旋鎖,可以實現(xiàn)具備公平性和可重入性質(zhì)的鎖。

58、什么時候應(yīng)該使用可重入鎖?

可重入鎖指在一個線程中可以多次獲取同一把鎖,比如:一個線程在執(zhí)行一個帶鎖的方法,該方法中又調(diào)用了另一個需要相同鎖的方法,則該線程可以直接執(zhí)行調(diào)用的方法,而無需重新獲得鎖;

場景1:如果已加鎖,則不再重復(fù)加鎖

場景2:如果發(fā)現(xiàn)該操作已在執(zhí)行,則嘗試等待一段時間,等待超時則不執(zhí)行

場景3:如果發(fā)現(xiàn)該操作已加鎖,則等待一個個加鎖(同步執(zhí)行,類似synchronized)

場景4:可中斷鎖

59、簡述synchronized鎖的等級方法鎖、對象鎖、類鎖?

線程進入同步代碼塊或方法時會自動獲得鎖,在退出時會釋放鎖。獲得內(nèi)置鎖的唯一途徑就是進入這個鎖保護的同步代碼塊或方法。內(nèi)置鎖是互斥鎖,最多只有一個線程能夠獲得該鎖,當(dāng)線程A嘗試去獲得線程B持有的內(nèi)置鎖時,線程A必須等待或阻塞,直到線程B釋放這個鎖,如果線程B不釋放,線程A將永遠等待。

方法鎖和對象鎖是一個東西,即只有方法鎖(對象鎖)和類鎖兩種鎖;

對象鎖和類鎖:對象鎖是用于對象實例方法,或一個對象實例上的,類鎖是用于類的靜態(tài)方法或類的class對象上的。類的對象實例可以有多個,但每個類只有一個class對象,所以不同對象實例的對象鎖是互不干擾的,但每個類只有一個類鎖。

60、Java中活鎖和死鎖有什么區(qū)別?

活鎖和死鎖類似,活鎖的線程狀態(tài)是不斷改變的,活鎖可認為是一種特殊的饑餓?;铈i和死鎖的主要區(qū)別是前者進程的狀態(tài)可以改變卻不能繼續(xù)執(zhí)行。

61、什么是死鎖(Deadlock)?導(dǎo)致線程死鎖的原因?如何確保N個線程可以訪問N個資源同時不導(dǎo)致死鎖?

兩個或以上線程都在互相等待對方執(zhí)行完畢才能繼續(xù)執(zhí)行時就發(fā)生死鎖。死鎖產(chǎn)生的四個必要條件:

1)互斥使用:當(dāng)資源被一個線程使用時,別的線程不能使用

2)不可搶占:資源請求者不能強制從資源占有者手中奪取資源,資源只能由占有者主動釋放。

3)請求和保持:當(dāng)資源請求者在請求其他資源的同時保持對原有資源的占有。

4)循環(huán)等待:存在一個等待環(huán)路隊列,P1占有P2的資源,P2占有P3的資源,P3占有P1的資源。

避免死鎖的方式:指定獲取鎖的順序,并強制線程按指定順序獲取鎖。

62、死鎖與饑餓的區(qū)別?

1)死鎖是同步的,饑餓是異步的。

2)死鎖可認為是兩個線程或進程同時在請求對方占有的資源,饑餓可認為是一個線程或進程在無限的等待另外多個線程或進程占有的但不會釋放的資源。

63、怎么檢測一個線程是否擁有鎖?

java.lang.Thread中有個方法holdsLock(),返回true表示當(dāng)前線程擁有某個對象的鎖。

64、如何實現(xiàn)分布式鎖?

1)基于數(shù)據(jù)庫。

2)基于緩存環(huán)境,redis、memcache等。

3)基于zookeeper。

65、有哪些無鎖數(shù)據(jù)結(jié)構(gòu),它們實現(xiàn)的原理是什么?

無鎖數(shù)據(jù)結(jié)構(gòu)的實現(xiàn)主要基于兩個方面:原子性操作和內(nèi)存訪問控制方法。

在構(gòu)建無鎖數(shù)據(jù)結(jié)構(gòu)時需要用到RMW操作,包括:compare-and-swap (CAS)、fetch-and-add (FAA)、test-and-set (TAS) 等。其中最基本的是CAS,其他操作可通過CAS實現(xiàn)。

66、讀寫鎖可用于什么應(yīng)用場景?

有多個讀數(shù)據(jù)的線程和單個寫數(shù)據(jù)線程的場景,在讀取數(shù)據(jù)的地方加讀鎖,在寫數(shù)據(jù)的地方加寫鎖。

67、Executors類是什么?Executor和Executors的區(qū)別?

Executors類提供了一系列工廠方法用于創(chuàng)建線程池,返回的線程池都實現(xiàn)了ExecutorService接口,ExecutorService繼承了Executor;

區(qū)別:

Executor是一個抽象層面的核心接口,將任務(wù)本身和執(zhí)行任務(wù)分離;

ExecutorService接口對Executor接口進行了擴展,提供了返回Future對象、終止、關(guān)閉線程池等方法。當(dāng)調(diào)用shutDown方法時,線程池會停止接受新的任務(wù),但會完成正在pending中的任務(wù)。

Future對象提供了異步執(zhí)行,無需等待任務(wù)執(zhí)行完成,只要提交需要執(zhí)行的任務(wù),然后在需要時檢查Future是否已經(jīng)有結(jié)果,如果任務(wù)已經(jīng)執(zhí)行完成,就可以通過Future.get()獲得執(zhí)行結(jié)果。Future.get()是一個阻塞式的方法,如果調(diào)用時任務(wù)還沒完成,會等待直到任務(wù)執(zhí)行結(jié)束。

Executors是一個工具類,類似于Collections,提供工廠方法來創(chuàng)建不同類型的線程池。

1)ExecutorService接口繼承了Executor接口,是Executor的子接口

2)Executor接口定義了execute()用來接收Runnable接口的對象,ExecutorService接口中submit()可以接受Runnable和Callable接口的對象。

3)Executor中的execute()不返回任何結(jié)果,ExecutorService中的submit()可以通過Future對象返回運算結(jié)果。

4)ExecutorService還提供用來控制線程池的方法。比如:調(diào)用shutDown()終止線程池。

5)Executors類提供工廠方法用來創(chuàng)建不同類型的線程池。

68、什么是Java線程轉(zhuǎn)儲(Thread Dump),如何得到它?

線程堆棧是虛擬機中線程(包括鎖)狀態(tài)的一個瞬間狀態(tài)的快照,即系統(tǒng)在某一個時刻所有線程的運行狀態(tài),包括每一個線程的調(diào)用堆棧,鎖的持有情況。

線程堆棧的信息都包含:

? ?1)線程名字,id,線程的數(shù)量等。

? ?2)線程的運行狀態(tài),鎖的狀態(tài)(鎖被哪個線程持有,哪個線程在等待鎖等)

? ?3)調(diào)用堆棧包含完整的類名,所執(zhí)行的方法,源代碼的行數(shù)等,不同虛擬機打印堆棧略有些不同。

獲取線程信息方式:

1、linux下執(zhí)行Kill -3 PID可以生成jvm的thread dump?

1) ?$ ps - ef | grep java ??獲得當(dāng)前正在運行的java進程pid

2) kill -3?<pid> ?

2、使用Java命令jstack獲得Thread dump

1)獲取java進程pid,獲取方式如jps -v命令,ps -ef|grep java命令,top命令等等。。

2)jstack -f <pid> ?

3、使用jvisualvm工具獲得Thread dump

4、在Windows下可以在JVM的console窗口上敲Ctrl-Break。根據(jù)不同的設(shè)置,thread dump會輸出到當(dāng)前控制臺上或應(yīng)用服務(wù)器的日志里。如果想將日志輸出到文件,可以修改tomcat/bin目錄下的“catalina.bat”,在文件最后的四個ACTION后增加“>>%CATALINA_BASE%/logs/thread_demp.out”,同時修改bin目錄下的startup.bat為:

rem call "%EXECUTABLE%" start "%CMD_LINE_ARGS%"?

call "%EXECUTABLE%" run "%CMD_LINE_ARGS%"

這樣就可以將日志輸出到logs下的thread_dump.out文件中。也可以下載相應(yīng)的分析工具對其進行分析。需要說明的一點是,將startup.bat修改為以上內(nèi)容后,關(guān)閉tomcat時,直接關(guān)閉DOS窗口就可以了,不用shutdown.bat。

69、如何在Java中獲取線程堆棧?

Java虛擬機提供了線程轉(zhuǎn)儲(thread dump)的后門,通過這個后門可以把線程堆棧打印出來。

命令:$jstack [option] pid >> 文件

70、說出3條在Java中使用線程的最佳實踐

1)對線程命名

2)將線程和任務(wù)分離,使用線程池執(zhí)行器來執(zhí)行Runnable或Callable。

3)使用線程池

71、在線程中你怎么處理不可捕捉異常

1)實現(xiàn)UncaughtExceptionHandler接口來捕獲拋出的異常

2)在Thread類中設(shè)置一個靜態(tài)域

在java多線程程序中,所有線程都不允許拋出未捕獲的checked exception(比如sleep時的InterruptedException),也就是說各個線程需要把自己的checked exception處理掉。

72、實際項目中使用多線程舉例。你在多線程環(huán)境中遇到的常見的問題是什么?你是怎么解決它的?

多線程中常遇到的有Memory-interface、競爭條件、死鎖、活鎖和饑餓。

73、請說出與線程同步以及線程調(diào)度相關(guān)的方法?

wait():使一個線程處于等待(阻塞)狀態(tài),并且釋放所持有的對象鎖;

sleep():使正在運行的線程處于睡眠狀態(tài),是靜態(tài)方法,調(diào)用此方法要處理InterruptedException;

notify():喚醒一個處于等待狀態(tài)的線程,調(diào)用此方法并不能確切的喚醒某個等待狀態(tài)的線程,而是由JVM確定喚醒哪個線程,而且與優(yōu)先級無關(guān);

notityAll():喚醒所有處于等待狀態(tài)的線程,該方法并不是將對象的鎖給所有線程,而是讓它們競爭,只有獲得鎖的線程才能進入就緒狀態(tài);

Lock接口提供了顯式的鎖機制(explicit lock),增強了靈活性以及對線程的協(xié)調(diào)。Lock接口中定義了加鎖lock()和解鎖unlock()方法,還提供了newCondition()方法來產(chǎn)生用于線程間通信的Condition對象;此外Java 5還提供了信號量機制(semaphore),信號量可以用來限制對某個共享資源進行訪問的線程數(shù)量。在對資源進行訪問前,線程必須得到信號量的許可(調(diào)用Semaphore.acquire());完成對資源的訪問后,線程必須向信號量歸還許可(調(diào)用Semaphore.release())。

74、如何在Windows和Linux上查找哪個線程使用的CPU時間最長?

windows上用任務(wù)管理器,linux下可以用top工具。 如果要查找具體的進程,可以用ps命令,如查找java:ps -ef |grep java

75、如何確保main()方法所在的線程是Java程序最后結(jié)束的線程?

需要用到Thread.join():

在一個線程中啟動另外一個線程的join(),當(dāng)前線程將會掛起,執(zhí)行被啟動的線程,直到被啟動的線程執(zhí)行完畢后,當(dāng)前線程才繼續(xù)執(zhí)行。

76、什么是競態(tài)條件? 舉個例子說明。

當(dāng)兩個線程競爭同一資源時,如果對資源的訪問順序敏感,就稱存在競態(tài)條件。

導(dǎo)致競態(tài)條件發(fā)生的代碼區(qū)稱作臨界區(qū)。在臨界區(qū)中使用適當(dāng)?shù)耐骄涂梢员苊飧倯B(tài)條件。臨界區(qū)實現(xiàn)方法有兩種,一種是用synchronized,一種是用Lock顯式鎖實現(xiàn)。

class Counter {

? ? protected long count = 0;

? ? public void add(long value) {

? ? ? ? this.count = this.count + value;

? ? }

}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,702評論 6 534
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,615評論 3 419
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 176,606評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,044評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 71,826評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,227評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,307評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,447評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,992評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,807評論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,001評論 1 370
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,550評論 5 361
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,243評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,667評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,930評論 1 287
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,709評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 47,996評論 2 374