今天來和大家討論一下并發多執行緒方面的知識,像死鎖啊,生命周期啊,執行緒池啊,可重用啊都有涉及,希望對大家有所幫助,
前言:
身為一個博客博主,最近逛了一下圈子,發現好多大佬都在分享并發多執行緒方面的技術,今天咱也跟一下風,寫一篇關于并發多執行緒方面的文章,本篇文章由淺入深,主要是讓大家能夠清楚的認識他們,希望對大家有所幫助,有什么意見或建議大家評論區里見,

另外本人整理收藏了20年多家公司面試知識點整理 ,以及各種Java核心知識點免費分享給大家,下方只是部分截圖 想要資料的話也可以點擊795983544領取 暗號CSDN,

1.什么是執行緒?

執行緒是作業系統能夠進行運算調度的最小單位,它被包含在行程之中,是行程中的實際運作單位,程式員可以通過它進行多處理器編程,你可以使用多執行緒對運算密集型任務提速,比如,如果一個執行緒完成一個任務要100毫秒,那么用十個執行緒完成改任務只需10毫秒,
2.執行緒和行程有什么區別?
執行緒是行程的子集,一個行程可以有很多執行緒,每條執行緒并行執行不同的任務,不同的行程使用不同的記憶體空間,而所有的執行緒共享一片相同的記憶體空間,每個執行緒都擁有單獨的堆疊記憶體用來存盤本地資料,
3.如何在Java中實作執行緒?
兩種方式:java.lang.Thread 類的實體就是一個執行緒但是它需要呼叫java.lang.Runnable介面來執行,由于執行緒類本身就是呼叫的Runnable介面所以你可以繼承java.lang.Thread 類或者直接呼叫Runnable介面來重寫run()方法實作執行緒,
4.Java 關鍵字volatile 與 synchronized 作用與區別?
1.volatile它所修飾的變數不保留拷貝,直接訪問主記憶體中的,
在Java記憶體模型中,有main memory,每個執行緒也有自己的memory (例如暫存器),為了性能,一個執行緒會在自己的memory中保持要訪問的變數的副本,這樣就會出現同一個變 量在某個瞬間,在一個執行緒的memory中的值可能與另外一個執行緒memory中的值,或者main memory中的值不一致的情況, 一個變數宣告為volatile,就意味著這個變數是隨時會被其他執行緒修改的,因此不能將它cache在執行緒memory中,
2.synchronized
當它用來修飾一個方法或者一個代碼塊的時候,能夠保證在同一時刻最多只有一個執行緒執行該段代碼,
一、當兩個并發執行緒訪問同一個物件object中的這個synchronized(this)同步代碼塊時,一個時間內只能有一個執行緒得到執行,另一個執行緒必須等待當前執行緒執行完這個代碼塊以后才能執行該代碼塊,
二、然而,當一個執行緒訪問object的一個synchronized(this)同步代碼塊時,另一個執行緒仍然可以訪問該object中的非synchronized(this)同步代碼塊,
三、尤其關鍵的是,當一個執行緒訪問object的一個synchronized(this)同步代碼塊時,其他執行緒對object中所有其它synchronized(this)同步代碼塊的訪問將被阻塞,
四、當一個執行緒訪問object的一個synchronized(this)同步代碼塊時,它就獲得了這個object的物件鎖,結果,其它執行緒對該object物件所有同步代碼部分的訪問都被暫時阻塞,
五、以上規則對其它物件鎖同樣適用.
5.有哪些不同的執行緒生命周期?
當我們在Java程式中新建一個執行緒時,它的狀態是New,當我們呼叫執行緒的start()方法時,狀態被改變為Runnable,執行緒調度器會為Runnable執行緒池中的執行緒分配CPU時間并且講它們的狀態改變為Running,其他的執行緒狀態還有Waiting,Blocked 和Dead,
6.你對執行緒優先級的理解是什么?
每一個執行緒都是有優先級的,一般來說,高優先級的執行緒在運行時會具有優先權,但這依賴于執行緒調度的實作,這個實作是和作業系統相關的(OS dependent),我們可以定義執行緒的優先級,但是這并不能保證高優先級的執行緒會在低優先級的執行緒前執行,執行緒優先級是一個int變數(從1-10),1代表最低優先級,10代表最高優先級,
7.什么是死鎖(Deadlock)?如何分析和避免死鎖?
死鎖是指兩個以上的執行緒永遠阻塞的情況,這種情況產生至少需要兩個以上的執行緒和兩個以上的資源,
分析死鎖,我們需要查看Java應用程式的執行緒轉儲,我們需要找出那些狀態為BLOCKED的執行緒和他們等待的資源,每個資源都有一個唯一的id,用這個id我們可以找出哪些執行緒已經擁有了它的物件鎖,
避免嵌套鎖,只在需要的地方使用鎖和避免無限期等待是避免死鎖的通常辦法,
8.什么是執行緒安全?Vector是一個執行緒安全類嗎?
如果你的代碼所在的行程中有多個執行緒在同時運行,而這些執行緒可能會同時運行這段代碼,如果每次運行結果和單執行緒運行的結果是一樣的,而且其他的變數的值也和預期的是一樣的,就是執行緒安全的,一個執行緒安全的計數器類的同一個實體物件在被多個執行緒使用的情況下也不會出現計算失誤,很顯然你可以將集合類分成兩組,執行緒安全和非執行緒安全的,Vector 是用同步方法來實作執行緒安全的, 而和它相似的ArrayList不是執行緒安全的,
9.Java中如何停止一個執行緒?
Java提供了很豐富的API但沒有為停止執行緒提供API,JDK 1.0本來有一些像stop(), suspend() 和 resume()的控制方法但是由于潛在的死鎖威脅因此在后續的JDK版本中他們被棄用了,之后Java API的設計者就沒有提供一個兼容且執行緒安全的方法來停止一個執行緒,當run() 或者 call() 方法執行完的時候執行緒會自動結束,如果要手動結束一個執行緒,你可以用volatile 布爾變數來退出run()方法的回圈或者是取消任務來中斷執行緒
10.什么是ThreadLocal?
ThreadLocal用于創建執行緒的本地變數,我們知道一個物件的所有執行緒會共享它的全域變數,所以這些變數不是執行緒安全的,我們可以使用同步技術,但是當我們不想使用同步的時候,我們可以選擇ThreadLocal變數,
每個執行緒都會擁有他們自己的Thread變數,它們可以使用get()\set()方法去獲取他們的默認值或者在執行緒內部改變他們的值,ThreadLocal實體通常是希望它們同執行緒狀態關聯起來是private static屬性,
11.Sleep()、suspend()和wait()之間有什么區別?
Thread.sleep()使當前執行緒在指定的時間處于“非運行”(Not Runnable)狀態,執行緒一直持有物件的監視器,比如一個執行緒當前在一個同步塊或同步方法中,其它執行緒不能進入該塊或方法中,如果另一執行緒呼叫了interrupt()方法,它將喚醒那個“睡眠的”執行緒,
注意:sleep()是一個靜態方法,這意味著只對當前執行緒有效,一個常見的錯誤是呼叫t.sleep(),(這里的t是一個不同于當前執行緒的執行緒),即便是執行t.sleep(),也是當前執行緒進入睡眠,而不是t執行緒,t.suspend()是過時的方法,使用suspend()導致執行緒進入停滯狀態,該執行緒會一直持有物件的監視器,suspend()容易引起死鎖問題,
object.wait()使當前執行緒出于“不可運行”狀態,和sleep()不同的是wait是object的方法而不是thread,呼叫object.wait()時,執行緒先要獲取這個物件的物件鎖,當前執行緒必須在鎖物件保持同步,把當前執行緒添加到等待佇列中,隨后另一執行緒可以同步同一個物件鎖來呼叫object.notify(),這樣將喚醒原來等待中的執行緒,然后釋放該鎖,基本上wait()/notify()與sleep()/interrupt()類似,只是前者需要獲取物件鎖,
12.什么是執行緒餓死,什么是活鎖?
當所有執行緒阻塞,或者由于需要的資源無效而不能處理,不存在非阻塞執行緒使資源可用,JavaAPI中執行緒活鎖可能發生在以下情形:
1,當所有執行緒在程式中執行Object.wait(0),引數為0的wait方法,程式將發生活鎖直到在相應的物件上有執行緒呼叫Object.notify()或者Object.notifyAll(),
2,當所有執行緒卡在無限回圈中,
13.什么是Java Timer類?如何創建一個有特定時間間隔的任務?
java.util.Timer是一個工具類,可以用于安排一個執行緒在未來的某個特定時間執行,Timer類可以用安排一次性任務或者周期任務,
java.util.TimerTask是一個實作了Runnable介面的抽象類,我們需要去繼承這個類來創建我們自己的定時任務并使用Timer去安排它的執行,
14.Java中的同步集合與并發集合有什么區別?
同步集合與并發集合都為多執行緒和并發提供了合適的執行緒安全的集合,不過并發集合的可擴展性更高,
在Java1.5之前程式員們只有同步集合來用且在多執行緒并發的時候會導致爭用,阻礙了系統的擴展性,
Java5介紹了并發集合像ConcurrentHashMap,不僅提供執行緒安全還用鎖分離和 內部磁區等現代技術提高了可擴展性,
15.同步方法和同步塊,哪個是更好的選擇?
同步塊是更好的選擇,因為它不會鎖住整個物件(當然你也可以讓它鎖住整個物件),同步方法會鎖住整個物件,哪怕這個類中有多個不相關聯的同步塊,這通常會導致他們停止執行并需要等待獲得這個物件上的鎖,
16.什么是執行緒池? 為什么要使用它?
創建執行緒要花費昂貴的資源和時間,如果任務來了才創建執行緒那么回應時間會變長,而且一個行程能創建的執行緒數有限,
為了避免這些問題,在程式啟動的時候就創建若干執行緒來回應處理,它們被稱為執行緒池,里面的執行緒叫作業執行緒,
從JDK1.5開始,Java API提供了Executor框架讓你可以創建不同的執行緒池,比如單執行緒池,每次處理一個任務;數目固定的執行緒池或者是快取執行緒池(一個適合很多生存期短的任務的程式的可擴展執行緒池),
17.Java中invokeAndWait 和 invokeLater有什么區別?
這兩個方法是Swing API 提供給Java開發者用來從當前執行緒而不是事件派發執行緒更新GUI組件用的,InvokeAndWait()同步更新GUI組件,比如一個進度條,一旦進度更新了,進度條也要做出相應改變,如果進度被多個執行緒跟蹤,那么就呼叫invokeAndWait()方法請求事件派發執行緒對組件進行相應更新,而invokeLater()方法是異步呼叫更新組件的,
18.多執行緒中的忙回圈是什么?
忙回圈就是程式員用回圈讓一個執行緒等待,不像傳統方法wait(), sleep() 或 yield() 它們都放棄了CPU控制,而忙回圈不會放棄CPU,它就是在運行一個慷訓圈,這么做的目的是為了保留CPU快取,
在多核系統中,一個等待執行緒醒來的時候可能會在另一個內核運行,這樣會重建快取,為了避免重建快取和減少等待重建的時間就可以使用它了,
19.Java記憶體模型是什么?
Java記憶體模型規定和指引Java程式在不同的記憶體架構、CPU和作業系統間有確定性地行為,它在多執行緒的情況下尤其重要,Java記憶體模型對一個執行緒所做的變動能被其它執行緒可見提供了保證,它們之間是先行發生關系,這個關系定義了一些規則讓程式員在并發編程時思路更清晰,比如,先行發生關系確保了:
執行緒內的代碼能夠按先后順序執行,這被稱為程式次序規則,
對于同一個鎖,一個解鎖操作一定要發生在時間上后發生的另一個鎖定操作之前,也叫做管程鎖定規則,
前一個對volatile的寫操作在后一個volatile的讀操作之前,也叫volatile變數規則,
一個執行緒內的任何操作必需在這個執行緒的start()呼叫之后,也叫作執行緒啟動規則,
一個執行緒的所有操作都會在執行緒終止之前,執行緒終止規則,
一個物件的終結操作必需在這個物件構造完成之后,也叫物件終結規則,
可傳遞性
20.Java中interrupted 和isInterruptedd方法的區別?
interrupted() 和 isInterrupted()的主要區別是前者會將中斷狀態清除而后者不會,Java多執行緒的中斷機制是用內部標識來實作的,呼叫Thread.interrupt()來中斷一個執行緒就會設定中斷標識為true,當中斷執行緒呼叫靜態方法Thread.interrupted()來檢查中斷狀態時,中斷狀態會被清零,
非靜態方法isInterrupted()用來查詢其它執行緒的中斷狀態且不會改變中斷狀態標識,簡單的說就是任何拋出InterruptedException例外的方法都會將中斷狀態清零,無論如何,一個執行緒的中斷狀態都有可能被其它執行緒呼叫中斷來改變,
21.Java中的同步集合與并發集合有什么區別?
同步集合與并發集合都為多執行緒和并發提供了合適的執行緒安全的集合,不過并發集合的可擴展性更高,在Java1.5之前程式員們只有同步集合來用且在多執行緒并發的時候會導致爭用,阻礙了系統的擴展性,Java5介紹了并發集合像ConcurrentHashMap,不僅提供執行緒安全還用鎖分離和內部磁區等現代技術提高了可擴展性,
不管是同步集合還是并發集合他們都支持執行緒安全,他們之間主要的區別體現在性能和可擴展性,還有他們如何實作的執行緒安全上,
同步HashMap, Hashtable, HashSet, Vector, ArrayList 相比他們并發的實作(ConcurrentHashMap, CopyOnWriteArrayList, CopyOnWriteHashSet)會慢得多,造成如此慢的主要原因是鎖, 同步集合會把整個Map或List鎖起來,而并發集合不會,并發集合實作執行緒安全是通過使用先進的和成熟的技術像鎖剝離,
比如ConcurrentHashMap 會把整個Map 劃分成幾個片段,只對相關的幾個片段上鎖,同時允許多執行緒訪問其他未上鎖的片段,
同樣的,CopyOnWriteArrayList 允許多個執行緒以非同步的方式讀,當有執行緒寫的時候它會將整個List復制一個副本給它,
如果在讀多寫少這種對并發集合有利的條件下使用并發集合,這會比使用同步集合更具有可伸縮性,
22.什么是執行緒池? 為什么要使用它?
創建執行緒要花費昂貴的資源和時間,如果任務來了才創建執行緒那么回應時間會變長,而且一個行程能創建的執行緒數有限,為了避免這些問題,在程式啟動的時候就創建若干執行緒來回應處理,它們被稱為執行緒池,里面的執行緒叫作業執行緒,從JDK1.5開始,Java API提供了Executor框架讓你可以創建不同的執行緒池,比如單執行緒池,每次處理一個任務;數目固定的執行緒池或者是快取執行緒池(一個適合很多生存期短的任務的程式的可擴展執行緒池)
執行緒池的作用,就是在呼叫執行緒的時候初始化一定數量的執行緒,有執行緒過來的時候,先檢測初始化的執行緒還有空的沒有,沒有就再看當前運行中的執行緒數是不是已經達到了最大數,如果沒有,就新分配一個執行緒去處理,
就像餐館中吃飯一樣,從里面叫一個服務員出來;但如果已經達到了最大數,就相當于服務員已經用盡了,那沒得辦法,另外的執行緒就只有等了,直到有新的“服務員”為止,
執行緒池的優點就是可以管理執行緒,有一個高度中樞,這樣程式才不會亂,保證系統不會因為大量的并發而因為資源不足掛掉,
23.Java中活鎖和死鎖有什么區別?
活鎖:一個執行緒通常會有會回應其他執行緒的活動,如果其他執行緒也會回應另一個執行緒的活動,那么就有可能發生活鎖,同死鎖一樣,發生活鎖的執行緒無法繼續執行,然而執行緒并沒有阻塞——他們在忙于回應對方無法恢復作業,這就相當于兩個在走廊相遇的人:甲向他自己的左邊靠想讓乙過去,而乙向他的右邊靠想讓甲過去,可見他們阻塞了對方,甲向他的右邊靠,而乙向他的左邊靠,他們還是阻塞了對方,
死鎖:兩個或更多執行緒阻塞著等待其它處于死鎖狀態的執行緒所持有的鎖,死鎖通常發生在多個執行緒同時但以不同的順序請求同一組鎖的時候,死鎖會讓你的程式掛起無法完成任務,
24.如何避免死鎖?
死鎖的發生必須滿足以下四個條件:
互斥條件:一個資源每次只能被一個行程使用,
請求與保持條件:一個行程因請求資源而阻塞時,對已獲得的資源保持不放,
不剝奪條件:行程已獲得的資源,在末使用完之前,不能強行剝奪,
回圈等待條件:若干行程之間形成一種頭尾相接的回圈等待資源關系,
三種用于避免死鎖的技術:
加鎖順序(執行緒按照一定的順序加鎖)
加鎖時限(執行緒嘗試獲取鎖的時候加上一定的時限,超過時限則放棄對該鎖的請求,并釋放自己占有的鎖)
死鎖檢測
25.notify()和notifyAll()有什么區別?
1,notify()和notifyAll()都是Object物件用于通知處在等待該物件的執行緒的方法,
2,void notify(): 喚醒一個正在等待該物件的執行緒,
3,void notifyAll(): 喚醒所有正在等待該物件的執行緒,
兩者的最大區別在于:
notifyAll使所有原來在該物件上等待被notify的執行緒統統退出wait的狀態,變成等待該物件上的鎖,一旦該物件被解鎖,他們就會去競爭,
notify他只是選擇一個wait狀態執行緒進行通知,并使它獲得該物件上的鎖,但不驚動其他同樣在等待被該物件notify的執行緒們,當第一個執行緒運行完畢以后釋放物件上的鎖,此時如果該物件沒有再次使用notify陳述句,即便該物件已經空閑,其他wait狀態等待的執行緒由于沒有得到該物件的通知,繼續處在wait狀態,直到這個物件發出一個notify或notifyAll,它們等待的是被notify或notifyAll,而不是鎖,
26.什么是可重入鎖(ReentrantLock)?
Java.util.concurrent.lock 中的 Lock 框架是鎖定的一個抽象,它允許把鎖定的實作作為Java 類,而不是作為語言的特性來實作,這就為Lock 的多種實作留下了空間,各種實作可能有不同的調度演算法、性能特性或者鎖定語意, ReentrantLock 類實作了Lock ,它擁有與synchronized 相同的并發性和記憶體語意,但是添加了類似鎖投票、定時鎖等候和可中斷鎖等候的一些特性,此外,它還提供了在激烈爭用情況下更佳的性能,(換句話說,當許多執行緒都想訪問共享資源時,JVM可以花更少的時候來調度執行緒,把更多時間用在執行執行緒上,)
Reentrant 鎖意味著什么呢?簡單來說,它有一個與鎖相關的獲取計數器,如果擁有鎖的某個執行緒再次得到鎖,那么獲取計數器就加1,然后鎖需要被釋放兩次才能獲得真正釋放,這模仿了synchronized 的語意;如果執行緒進入由執行緒已經擁有的監控器保護的synchronized 塊,就允許執行緒繼續進行,當執行緒退出第二個(或者后續)synchronized塊的時候,不釋放鎖,只有執行緒退出它進入的監控器保護的第一個synchronized 塊時,才釋放鎖,
27.讀寫鎖可以用于什么應用場景?
讀寫鎖可以用于 “多讀少寫” 的場景,讀寫鎖支持多個讀操作并發執行,寫操作只能由一個執行緒來操作
ReadWriteLock對向資料結構相對不頻繁地寫入,但是有多個任務要經常讀取這個資料結構的這類情況進行了優化,ReadWriteLock使得你可以同時有多個讀取者,只要它們都不試圖寫入即可,如果寫鎖已經被其他任務持有,那么任何讀取者都不能訪問,直至這個寫鎖被釋放為止,
ReadWriteLock 對程式性能的提高主要受制于如下幾個因素:
1,資料被讀取的頻率與被修改的頻率相比較的結果,
2,讀取和寫入的時間
3,有多少執行緒競爭
4,是否在多處理機器上運行
總結:
現在的互聯網公司都很重視高并發高可用技術,理論知識需要我們去清楚理解,而最重要的還是作業中所運用到的,那句話說的好,面試造火箭,作業擰螺絲,希望大家都有造火箭的一天,那就說明你已經很牛皮了,其實我寫了這么多,只是我自己的總結,并不一定適用于所有人,相信經過一些面試,大家都會有這些感觸,
另外本人整理收藏了20年多家公司面試知識點整理 共127頁的PDF 以及各種知識點整理 免費分享給大家,想要資料的話點擊795983544暗號CSDN自行領取,今天的分享就到這了,希望大家多多支持我,一個不甘平凡的小碼農!

轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/138997.html
標籤:其他
