突擊檢查:Java面試之多執行緒&併發篇(11)

码路编程發表於2024-12-05

前言

本來想著給自己放鬆一下,刷刷部落格,突然被幾道面試題難倒!什麼是多執行緒中的上下文切換?什麼是Daemon執行緒?它有什麼意義?樂觀鎖和悲觀鎖的理解及如何實現,有哪些實現方式?似乎有點模糊了,那就大概看一下面試題吧。好記性不如爛鍵盤

*** 12萬字的java面試題整理 ***
*** java核心面試知識整理 ***
*** Java高頻面試講解影片(知識涵蓋齊全) ***

什麼是多執行緒中的上下文切換?

在上下文切換過程中,CPU會停止處理當前執行的程式,並儲存當前程式執行的具體位置以便之後繼續執行。從這個角度來看,上下文切換有點像我們同時閱讀幾本書,在來回切換書本的同時我們需要記住每本書當前讀到的頁碼。
在程式中,上下文切換過程中的“頁碼”資訊是儲存在程序控制塊(PCB)中的。PCB還經常被稱作“切換楨”(switchframe)。“頁碼”資訊會一直儲存到CPU的記憶體中,直到他們被再次使用。
上下文切換是儲存和恢復CPU狀態的過程,它使得執行緒執行能夠從中斷點恢復執行。上下文切換是多工作業系統和多執行緒環境的基本特徵。

什麼是Daemon執行緒?它有什麼意義?

所謂後臺(daemon)執行緒,也叫守護執行緒,是指在程式執行的時候在後臺提供一種通用服務的執行緒,並且這個執行緒並不屬於程式中不可或缺的部分。
因此,當所有的非後臺執行緒結束時,程式也就終止了,同時會殺死程序中的所有後臺執行緒。反過來說, 只要有任何非後臺執行緒還在執行,程式就不會終止。
必須線上程啟動之前呼叫setDaemon()方法,才能把它設定為後臺執行緒。注意:後臺程序在不執行finally子句的情況下就會終止其run()方法。
比如:JVM的垃圾回收執行緒就是Daemon執行緒,Finalizer也是守護執行緒。

樂觀鎖和悲觀鎖的理解及如何實現,有哪些實現方式?

悲觀鎖:總是假設最壞的情況,每次去拿資料的時候都認為別人會修改,所以每次在拿資料的時候都會上鎖,這樣別人想拿這個資料就會阻塞直到它拿到鎖。
傳統的關係型資料庫裡邊就用到了很多這種鎖機制,比如行鎖,表鎖等,讀鎖,寫鎖等,都是在做操作之前先上鎖。再比如Java裡面的同步原語synchronized關鍵字的實現也是悲觀鎖。
樂觀鎖:顧名思義,就是很樂觀,每次去拿資料的時候都認為別人不會修改,所以不會上鎖,但是在更新的時候會判斷一下在此期間別人有沒有去更新這個資料,可以使用版本號等機制。
樂觀鎖適用於多讀的應用型別,這樣可以提高吞吐量,像資料庫提供的類似於write_condition機制,其實都是提供的樂觀鎖。
在Java中java.util.concurrent.atomic包下面的原子變數類就是使用了樂觀鎖的一種實現方式CAS實現的。

樂觀鎖的實現方式:

1、使用版本標識來確定讀到的資料與提交時的資料是否一致。提交後修改版本標識,不一致時可以採取丟棄和再次嘗試的策略。
2、java中的Compare and Swap即CAS ,當多個執行緒嘗試使用CAS同時更新同一個變數時,只有其中一個執行緒能更新變數的值,而其它執行緒都失敗,失敗的執行緒並不會被掛起,而是被告知這次競爭中失敗,並可以再次嘗試。
CAS 操作中包含三個運算元 —— 需要讀寫的記憶體位置(V)、進行比較的預期原值(A)和擬寫入的新值(B)。如果記憶體位置V的值與預期原值A相匹配,那麼處理器會自動將該位置值更新為新值B。否則處理器不做任何操作。

CAS缺點:

  1. ABA問題:比如說一個執行緒one從記憶體位置V中取出A,這時候另一個執行緒two也從記憶體中取出
    A,並且two進行了一些操作變成了B,然後two又將V位置的資料變成A,這時候執行緒one進行
    CAS操作發現記憶體中仍然是A,然後one操作成功。儘管執行緒one的CAS操作成功,但可能存在
    潛藏的問題。從Java1.5開始JDK的atomic包裡提供了一個類AtomicStampedReference來解決
    ABA問題。
  2. 迴圈時間長開銷大:對於資源競爭嚴重(執行緒衝突嚴重)的情況,CAS自旋的機率會比較大,
    從而浪費更多的CPU資源,效率低於synchronized。
  3. 只能保證一個共享變數的原子操作:當對一個共享變數執行操作時,我們可以使用迴圈CAS的方式來保證原子操作,但是對多個共享變數操作時,迴圈CAS就無法保證操作的原子性,這個時候就可以用鎖。

相關文章