本頭條號會不定期分享程序員的面試經驗,以及程序員需要掌握的技術乾貨,喜歡就關注我吧~
在Java程序員面試中,我們經常會被面試官問到如下問題:
為什麼HashMap不是線程安全的?
為什麼ConcurrentHashMap是線程安全的?
怎麼樣算線程安全?
什麼是悲觀鎖和樂觀鎖?
死鎖是什麼意思?
什麼情況下會發生死鎖?
並行和併發的區別是什麼?
等等....
今天的主要內容是Java多線程編程中的基本概念總結,看完今天的內容你應該就能回答上面的這些問題了。
同步(Synchronous)和異步(Asynchronous)
同步和異步通常用來形容一次方法調用。同步方法調用一旦開始,調用者必須等到方法調用返回後,才能繼續後續的行為。異步方法調用更像是一個消息傳遞,一旦開始,方法調用就會立即返回,調用者就可以繼續後續的操作。
併發(Concurrency)和並行(Parallelism)
併發偏重於多個任務交替執行,而多個任務之間有可能還是串行的。並行是真正意義上的“同時執行”。
臨界區(Critical Section)
臨界區用來表示一種公共資源或共享數據,可以被多個線程使用,但是每一次只能有一個線程使用它。一旦臨界區資源被佔用,其他線程要想使用這個資源必須等待。
阻塞(Blocking)和非阻塞(Non-Blocking)
阻塞和非阻塞通常用來形容多線程間的相互影響。一個線程是阻塞的,那麼在其他線程釋放資源之前,當前線程無法繼續執行,線程會被掛起。非阻塞與之相反,所有的線程都會不斷向前執行。
死鎖(Deadlock)、飢餓(Starvation)和活鎖(Livelock)
死鎖、飢餓和活鎖都屬於多線程的活躍性問題。
死鎖是指兩個或以上線程執行時,因爭奪資源而互相等待的現象,若無外力作用,它們都將無法推進下去。
飢餓是指一個或多個線程因為種種原因無法獲得所需資源,導致一直無法執行,如優先級太低。
活鎖是指線程之間沒有被阻塞,但由於某些條件沒有滿足,導致一直重複嘗試,失敗,嘗試,失敗,比如互相謙讓。活鎖有一定機率解開,而死鎖則無法自行解開。解決活鎖的辦法:在重試機制中引入隨機性。
悲觀鎖(Pessimistic Locking)和樂觀鎖(Optimistic Locking)
悲觀鎖是指不管是否發生多線程衝突,只要存在這種可能,就每次訪問都加鎖。即假設衝突一定會發生。
樂觀鎖則是通過標記值控制,每次操作前通過標記值判斷是否是最新內容,最新內容就可以操作,不是最新的就繼續循環判斷標記值,直到是最新內容。即假設衝突不會發生。
樂觀鎖的優點是不會發生死鎖,並且在適量衝突的情況下,效率更高。樂觀鎖的實現:CAS、MVCC(多版本併發控制)。
線程安全(Thread safe)和競態條件(Race Condition)
當多個線程訪問某個類時,不管運行時環境採用何種調度方式或者這些線程將如何交替執行,並且在主調代碼中不需要任何額外的同步或協調,這個類都能表現出正確的行為,那麼就稱這個類是線程安全的。[Java併發編程實戰]
在線程安全的定義中,最核心的概念就是正確性。在線程安全類中封裝了必要的同步機制,因此客戶端無需進一步採取同步措施。
競態條件(race condition):在併發編程中,由於不恰當的執行時序而出現不正確的結果的情況。
最常見的競態條件是“先檢查後執行”。要避免競態條件,就必須在某個線程修改該變量時,通過某種方式防止其他線程使用這個變量,從而確保其他線程只能在修改操作完成之前或之後讀取和修改狀態,而不是在修改狀態的過程中。
內置鎖(monitor)
每個Java對象都可以用作一個實現同步的鎖,這些鎖被稱為內置鎖或監視器。線程在進入同步代碼塊之前會自動獲得鎖,並且在退出同步代碼塊時自動釋放鎖,不論是通過正常的控制路徑退出還是通過從代碼塊中拋出異常退出。
Java內置鎖相當於一種互斥體(或互斥鎖),最多隻有一個線程能持有這種鎖。內置鎖是可重入的。
完。