Java併發相關面試題

1、ConcurrentHashMap原理,如何保證線程安全
參考
使用CAS和synchronized保證線程安全
2、紅黑樹原理
參考
3、synchronized
(1)synchronized是可重入鎖。
可重入鎖顧名思義表示可重新反覆進入的鎖,但僅限於當前線程。是爲了解決自己鎖死自己的情況。
一個類中的同步方法調用另一個同步方法,假如 Synchronized 不支持重入,進入 method1 方法時當前線程獲得鎖,method1 方法裏面執行 method2 時當前線程又要去嘗試獲取鎖,如果不支持重入,它就要等釋放,把自己阻塞,導致自己鎖死自己。
(2)synchronized鎖對象是什麼?
當synchronized作用在實例方法時,監視器鎖(monitor)便是對象實例(this);
當synchronized作用在靜態方法時,監視器鎖(monitor)便是對象的Class實例,因爲Class數據存在於永久代,因此靜態方法鎖相當於該類的一個全局鎖;
當synchronized作用在某一個對象實例時,監視器鎖(monitor)便是括號括起來的對象實例;
synchronized 內置鎖 是一種 對象鎖(鎖的是對象而非引用變量),作用粒度是對象。
(3)synchronized底層原理
synchronized代碼塊反編譯後會發現代碼塊被monitorenter和monitorexit包裹。當monitor被佔用時就會處於鎖定狀態,線程執行monitorenter指令時嘗試獲取monitor的所有權,過程如下:
A、如果monitor的進入數爲0,則該線程進入monitor,然後將進入數設置爲1,該線程即爲monitor的所有者;
B、如果線程已經佔有該monitor,只是重新進入,則進入monitor的進入數加1;
C、如果其他線程已經佔用了monitor,則該線程進入阻塞狀態,直到monitor的進入數爲0,再重新嘗試獲取monitor的所有權;
參考
4、synchronized與ReentrantLock
相同點:都是阻塞式的同步,都可重入
不同點:
A、ReentrantLock更加靈活。
a、synchronized是非公平鎖,ReentrantLock默認非公平,可選擇爲公平鎖。
b、等待可中斷,持有鎖的線程長期不釋放的時候,正在等待的線程可以選擇放棄等待,這相當於Synchronized來說可以避免出現死鎖的情況。通過lock.lockInterruptibly()來實現這個機制。
c、鎖綁定多個條件,一個ReentrantLock對象可以同時綁定對個對象。ReenTrantLock提供了一個Condition(條件)類,用來實現分組喚醒需要喚醒的線程們,而不是像synchronized要麼隨機喚醒一個線程要麼喚醒全部線程。
B、對於Synchronized來說,它是java語言的關鍵字,是原生語法層面的互斥,需要jvm實現。而ReentrantLock它是JDK 1.5之後提供的API層面的互斥鎖,需要lock()和unlock()方法配合try/finally語句塊來完成
C、在Synchronized優化以前,synchronized的性能是比ReenTrantLock差很多的,但是自從Synchronized引入了偏向鎖,輕量級鎖(自旋鎖)後,兩者的性能就差不多了,在兩種方法都可用的情況下,官方甚至建議使用synchronized,其實synchronized的優化我感覺就借鑑了ReenTrantLock中的CAS技術。都是試圖在用戶態就把加鎖問題解決,避免進入內核態的線程阻塞。
D、ReentrantLock是一種自旋鎖,通過循環調用CAS操作來說實現,性能比較好也是因爲避免了使線程進入內核態的阻塞狀態。
參考
5、CAS的原理
CAS(Conmpare And Swap)是用於實現多線程同步的原子指令。它將內存位置的內容與給定值進行比較,只有在相同的情況下,將該內存位置的內容修改爲新的給定值。 這是作爲單個原子操作完成的。 原子性保證新值基於最新信息計算; 如果該值在同一時間被另一個線程更新,則寫入將失敗。 操作結果必須說明是否進行替換。
CAS操作都是通過sun包下Unsafe類實現,而Unsafe類中的方法都是native方法,是CPU指令級的操作,本身可以保證原子性。
6、ABA問題
CAS可以有效的提升併發的效率,但同時也會引入ABA問題。
ABA問題的解釋:如線程1從內存X中取出A,這時候另一個線程2也從內存X中取出A,並且線程2進行了一些操作將內存X中的值變成了B,然後線程2又將內存X中的數據變成A,這時候線程1進行CAS操作發現內存X中仍然是A,然後線程1操作成功。雖然線程1的CAS操作成功,但是整個過程就是有問題的。比如老公銀行卡里原來有5000,工資發了5000,老婆轉走5000,餘額還是5000,這時候老公就會認爲工資沒發。
解決辦法:AtomicStampReference
AtomicStampReference在cas的基礎上增加了一個標記stamp,使用這個標記可以用來覺察數據是否發生變化,相當於給數據增加了一個版本號。
7、強引用、軟引用、弱引用、虛引用
強引用:默認,如果一個對象具有強引用,那就類似於必不可少的物品,不會被垃圾回收器回收。當內存空間不足,Java虛擬機寧願拋出OutOfMemoryError錯誤,使程序異常終止,也不回收這種對象。
軟引用:可以理解爲有用但並不是必需。對於軟引用關聯着的對象,只有在內存不足的時候JVM纔會回收該對象。很適合用來實現緩存:比如網頁緩存、圖片緩存等。
弱引用:弱引用也是用來描述非必需對象的,當JVM進行垃圾回收時,無論內存是否充足,都會回收被弱引用關聯的對象。在java中,用java.lang.ref.WeakReference類來表示。
應用場景:如果一個對象是偶爾的使用,並且希望在使用時隨時就能獲取到,但又不想影響此對象的垃圾收集,那麼應該用 Weak Reference 來記住此對象。或者想引用一個對象,但是這個對象有自己的生命週期,你不想介入這個對象的生命週期,這時候就應該用弱引用,這個引用不會在對象的垃圾回收判斷中產生任何附加的影響。
虛引用:不影響對象的生命週期。在java中用java.lang.ref.PhantomReference類表示。如果一個對象與虛引用關聯,則跟沒有引用與之關聯一樣,在任何時候都可能被垃圾回收器回收。虛引用主要用來跟蹤對象被垃圾回收的活動。必須和引用隊列關聯使用,當垃圾回收器準備回收一個對象時,如果發現它還有虛引用,就會把這個虛引用加入到與之 關聯的引用隊列中。
參考
8、volitile是做什麼的?
volatile 是一個類型修飾符。volatile 的作用是作爲指令關鍵字,確保本條指令不會因編譯器的優化而省略。
特點:

  • 保證了不同線程對這個變量進行操作時的可見性,即一個線程修改了某個變量的值,這新值對其他線程來說是立即可見的。(實現可見性)
  • 禁止進行指令重排序。(實現有序性)
  • volatile 只能保證對單次讀/寫的原子性。i++ 這種操作不能保證原子性。
    參考

9、公平鎖與非公平鎖
10、樂觀鎖與悲觀鎖
11、wait、notify爲什麼是Object的方法
個人認爲wait、notify都是對象鎖的功能,放在Object中更加符合單一職責原則。
參考
12、wait和sleep的區別

  • 這兩個方法來自不同的類分別是Thread和Object
  • 最主要是sleep方法沒有釋放鎖,而wait方法釋放了鎖,使得其他線程可以使用同步控制塊或者方法。
  • wait,notify和notifyAll只能在同步控制方法或者同步控制塊裏面使用,而sleep可以在任何地方使用(使用範圍)
  • sleep必須捕獲異常,而wait,notify和notifyAll不需要捕獲異常
  • sleep是Thread類的靜態方法。sleep的作用是讓線程休眠制定的時間,在時間到達時恢復,也就是說sleep將在接到時間到達事件事恢復線程執行。wait是Object的方法,也就是說可以對任意一個對象調用wait方法,調用wait方法將會將調用者的線程掛起,直到其他線程調用同一個對象的notify方法纔會重新激活調用者。
    掛起(sleep)與阻塞(wait)的區別
    參考

13、Java內存模型
關鍵點:區分主內存和工作內存、volitile
參考

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章