Java併發常見面試題
synchronized 和volatile 關鍵字的作用
volatile 本質是在告訴jvm 當前變數在暫存器(工作記憶體)中的值是不確定的,需要從主存中讀取;volatile 保證了不同執行緒對這個變數進行操作時的可見性,即一個執行緒修改了某個變數的值,這新值對其他執行緒來說是立即可見的。2)禁止進行指令重排序。
synchronized 則是鎖定當前變數,只有當前執行緒可以訪問該變數,其他執行緒被阻塞住。
1.volatile 僅能使用在變數級別;synchronized 則可以使用在變數、方法、和類級別的
2.volatile 僅能實現變數的修改可見性,並不能保證原子性;synchronized 則可以保證變數的修改可見性和原子性
3.volatile 不會造成執行緒的阻塞;synchronized 可能會造成執行緒的阻塞。
4.volatile 標記的變數不會被編譯器優化;synchronized 標記的變數可以被編譯器優化
對於原子性,需要強調一點,也是大家容易誤解的一點:對volatile變數的單次讀/寫操作可以保證原子性的,但是並不能保證i++這種操作的原子性,因為本質上i++是讀、寫兩次操作。(能保證原子性的:在boolean中 迴圈的時候 while (!close) 用volatile修飾close,close的一旦改變就會打破迴圈)
wait()和seelp()方法的區別
最大的不同是在等待時wait 會釋放鎖,而sleep 一直持有鎖。wait 通常被用於執行緒間互動,sleep 通常被用於暫停執行。
sleep():屬於Thread類,可以在任何地方使用,必須捕獲異常
wait(): 屬於Object類,只能在同步方法或同步控制塊中用,不需捕獲異常,只在while裡面使用,不在if裡面使用
notify和notifyAll的區別
notify()只會隨機喚醒一個睡眠執行緒,並不一定是我們想要喚醒的執行緒。
notifyAll(),喚醒所有等待中的執行緒
開啟執行緒的三種方式
1、繼承Thread類,重寫run方法。用start方法啟動執行緒
2、實現Runnable介面,實現run方法。用new Thread(Runnable target).start()來啟動
3、實現Callable介面
實現Runnable介面優勢:
1)適合多個相同的程式程式碼的執行緒去處理同一個資源
2)可以避免java中的單繼承的限制
3)增加程式的健壯性,程式碼可以被多個執行緒共享,程式碼和資料獨立。
繼承Thread類優勢:
1)可以將執行緒類抽象出來,當需要使用抽象工廠模式設計時。
2)多執行緒同步
run()和start()方法區別
run()方法只是一個普通的方法呼叫,不會開啟新的執行緒。
start()會開啟新的執行緒,分配新的資源,真正實現了多執行緒執行。
線上程的生命週期中,當呼叫了執行緒物件的start方法之後,該執行緒就進入了就緒狀態;
執行緒排程程式將處於就緒狀態的執行緒設定為當前執行緒,此時執行緒就進入了執行狀態,開始執行run函式當中的程式碼。
執行緒如何關閉?
1.設定退出標誌,使執行緒正常退出,也就是當run()方法完成後執行緒終止
2.使用interrupt()方法中斷執行緒
3.使用stop方法強行終止執行緒(不推薦使用,是不安全的!)
如何保證執行緒安全?如何防止執行緒的記憶體洩漏?
所謂執行緒安全無 非是要控制多個執行緒對某個資源的有序訪問或修改。總結java的記憶體模型,要解決兩個主要的問題:可見性和有序性。
執行緒死鎖的4個條件?死鎖的概念,怎麼避免死鎖
概念:當執行緒A持有獨佔鎖a,並嘗試去獲取獨佔鎖b的同時,執行緒B持有獨佔鎖b,並嘗試獲取獨佔鎖a的情況下,就會發生AB兩個執行緒由於互相持有對方需要的鎖,而發生的阻塞現象,我們稱為死鎖。
四個必要條件:
1.互斥條件:一個資源每次只能被一個執行緒使用。
2.請求與保持條件:一個執行緒因請求資源而阻塞時,對已獲得的資源保持不放。
3.不剝奪條件:執行緒已獲得的資源,在未使用完之前,不能強行剝奪。
4.迴圈等待條件:若干執行緒之間形成一種頭尾相接的迴圈等待資源關係。
在併發程式中,避免了邏輯中出現複數個執行緒互相持有對方執行緒所需要的獨佔鎖的的情況,就可以避免死鎖。
程序和執行緒的區別
(1)排程:程序是擁有資源的基本單位。,執行緒是程序排程和分配的基本單位
(2)併發性:不僅程序之間可以併發執行,同一個程序的多個執行緒之間也可併發執行。
(3)擁有資源:程序是擁有資源的一個獨立單位,執行緒不擁有系統資源,但可以訪問隸屬於程序的資源。
(4)系統開銷:在建立或撤消程序時,由於系統都要為之分配和回收資源,導致系統的開銷明顯大於建立或撤消執行緒時的開銷。
如何控制某個方法允許併發訪問執行緒的個數?
什麼是執行緒池?如何使用?什麼時候使用執行緒池?
執行緒池就是事先將多個執行緒物件放到一個容器中,當使用的時候就不用new 執行緒而是直接去池中拿執行緒即可,節省了開闢子執行緒的時間,提高的程式碼執行效率。
單個任務處理時間比較短,需要處理的任務數量很大的時候使用。
執行緒池的好處?為什麼要使用執行緒池?
a:降低資源消耗。通過重複利用已建立的執行緒降低執行緒建立和銷燬造成的消耗。
b:提高響應速度。當任務到達時,任務可以不需要的等到執行緒建立就能立即執行。
c:提高執行緒的可管理性。執行緒是稀缺資源,如果無限制的建立,不僅會消耗系統資源,還會降低系統的穩定性,使用執行緒池可以進行統一的分配,調優和監控。
說說幾種常見的執行緒池及使用場景。 四種執行緒池
a:newCachedThreadPool
建立一個可根據需要建立新執行緒的執行緒池,但是在以前構造的執行緒可用時將重用它們。
- 適用:執行很多短期非同步的小程式或者負載較輕的伺服器
b:newFixedThreadPool
建立一個指定工作執行緒數量的執行緒池
- 適用:執行長期的任務,效能好很多
c:newScheduledThreadPool
建立一個執行緒池,它可安排在給定延遲後執行命令或者定期地執行。
- 適用:一個任務一個任務執行的場景
d:newSingleThreadExecutor
建立一個使用單個 worker 執行緒的 Executor,以無界佇列方式來執行該執行緒。
- 適用:週期性執行任務的場景
執行緒池都有哪幾種工作佇列?
怎麼理解無界佇列和有界佇列?
執行緒池中的幾種重要的引數及流程說明。
synchrolzie關鍵字和Lock的區別你知道嗎?為什麼Lock的效能好一些?
ConCurrentHashMap實現
繼續更新中.........