4 個場景詳解頭條等大廠面試中的死鎖問題,通俗易懂

根據不少同學的面試反饋,最近阿里和字節跳動面試時都對多線程相關的問題進行了重點考察,並且面試官都問到了死鎖問題。如字節跳動考察的問題是:

  • 什麼是線程死鎖?
  • 死鎖如何產生?
  • 如何避免線程死鎖?
本文便就此問題進行分析,將用盡可能通俗的語言由淺入深地幫助大家理解死鎖,瞭解其產生的原理與對應的解決方案。


01 什麼是線程死鎖

線程死鎖是指由於兩個或者多個線程互相持有對方所需要的資源,導致這些線程處於等待狀態,無法前往執行。當線程進入對象的 synchronized 代碼塊時,便佔有了資源,直到它退出該代碼塊或者調用 wait 方法,才釋放資源,在此期間,其他線程將不能進入該代碼塊。當線程互相持有對方所需要的資源時,會互相等待對方釋放資源,如果線程都不主動釋放所佔有的資源,將產生死鎖。
640?wx_fmt=jpeg


02 死鎖如何產生

# 場景一

星期日早上十點半,你在公路上開車,這是一條窄路,只能容納一輛車。這時,迎面又駛來一輛車,你們都走到一半,誰也不想倒回去,於是各不相讓,陷入無盡的等待。

# 場景二

你和她吵架了,誰也不理誰,甚至晚飯時間都各自煮飯。你在炒京醬肉絲,她在做蔥烤鯽魚。炒到一半你發現小蔥被她全部拿走了,於是你默默等待她做好菜後再去拿,殊不知她也在等待你炒完菜後來拿醬油。

# 場景三

你和四個好朋友坐在圓形餐桌旁,你們只做兩件事情:喫飯,或者思考。喫飯的時候,你們就停止思考。思考的時候,也停止喫飯。每個人面前有一碗蘭州拉麪,並且每個人左右兩邊各有一根筷子。你們必須要拿到兩根筷子才能開始吃麪。喫完後再放下筷子,讓別人可以使用。吃了一會之後,每個人都拿起了自己左手邊的筷子,導致每個人都只有一根筷子,並且等待別人喫完放下筷子給自己。可惜,沒有人喫到面,所以沒有人會放下筷子。(著名的哲學家就餐問題)

# 場景四

你有兩個線程 A 和 B ,各自在加鎖的狀態下運行。A 持有一部分資源,並且等待 B 線程中的資源以完成自己的工作,而此時 B 線程也在等待 A 中的資源以完成自己的工作。由於他們都是鎖定狀態,所以他們必須完成了自己的工作後,自己持有的資源才能釋放。於是線程無休止地等待,導致死鎖。
代碼如下
public class JavaTest {    @Test    public void test() {        final Object lockA = new Object();        final Object lockB = new Object();        new Thread(new Runnable() {            @Override            public void run() {                synchronized (lockA) {                    try {                        Thread.sleep(1000);                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                    synchronized (lockB) {                    }                    System.out.println("finish A");                }            }        }).start();        new Thread(new Runnable() {            @Override            public void run() {                synchronized (lockB) {                    try {                        Thread.sleep(1000);                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                    synchronized (lockA) {                    }                    System.out.println("finish B");                }            }        }).start();        try {            Thread.sleep(10000);        } catch (InterruptedException e) {            e.printStackTrace();        }    }}
此程序中,線程 A 持有 lockA 對象,並請求 lockB 對象;線程 B 持有 lockB 對象,並請求 lockA 對象。由於他們都在等待對方釋放資源,所以會產生死鎖。運行程序,將發現控制檯無法打印出 "finish A" 和 "finish B" 消息。
這些都是程序員在工作或生活中會遇到的問題,人生就像是一個進程,時間是我們的主線程,期間做的每一件事都是開啓的一個子線程。當多件事衝突時,併發問題就產生了。上述問題都指向同一類併發問題:死鎖
產生死鎖的的四個條件如下
1、互斥條件:一個資源每次只能被一個進程使用;
2、請求與保持條件:一個進程因請求資源而阻塞時,對已獲得的資源保持不放;
3、不剝奪條件:進程已獲得的資源,在沒使用完之前,不能強行剝奪;
4、循環等待條件:多個進程之間形成一種互相循環等待資源的關係。
併發帶來壓力,有的人或有的程序,會因爲承受不住壓力而崩潰,情緒崩潰和程序崩潰沒什麼兩樣。當然,不論是做人還是寫程序,面對問題時,正確的做法都應是採取策略,解除死鎖。


03 如何避免線程死鎖

# 方案一

你想起書中所言:退一步海闊天空。但你也深知公平好過忍讓。正值周賽時間,你搖下車窗,對對面的兄弟喊道:咱來比賽一場力扣周賽,誰輸了誰倒出去讓另一個人過吧!於是你們打開力扣,開始答題。半小時後,你憑藉高超的代碼水平 AC 了全部題目。對面司機對你拱手道:技不如人,甘拜下風。於是他倒了回去,讓出了自己的一半路。最終你們都得以順利通行。

# 方案二

你在炒菜時發現沒有小蔥,於是你換位思考,想到她會不會也缺少自己用着的材料。雖然她還在和你冷戰,但你勸解自己一個大老爺們不應該和女孩子置氣,於是你主動把自己用着的所有材料拿給了她。她感受到你設身處地爲她着想,大爲感動,你們和好如初。之後她爲你們兩個人一起炒了京醬肉絲和蔥烤鯽魚。

# 方案三

你和你的朋友們決定給筷子編上號:1~5。規定每個人拿筷子時必須先拿到自己兩邊的筷子中號碼小的那一根,再去拿號碼大的那一根。如果小的那一根沒有拿到,不能先拿大的。當你們開始喫飯時,由於數字 5 不可能被一個人單獨拿到。因爲他旁邊的另一根筷子編號必定比 5 小,所以不會再出現每個人都拿着一根的無限等待情形。

# 方案四

你在運行兩個線程前,預先將線程 A 和 B 中的資源拷貝一份,讓他們不需互相等待對方的資源,於是兩個線程都得以順利運行。
這四種方案分別破壞了上述四個條件之一。
這也是解決死鎖問題的四種方法
1、破壞不剝奪條件:讓對面的司機放棄了自己已有的資源。
2、破壞請求與保持條件:在自己需要的材料缺少時,主動放棄自己持有的資源,防止出現互相等待。
3、破壞循環等待條件:由於筷子指定了編號和獲取規則,所以每個鎖定狀態都將按照順序執行,於是便杜絕了環路等待條件。
4、破壞互斥條件:由於每次使用時都拷貝一份,所以一個資源可以被多個進程使用。

04 常考面試考點

面試中對死鎖的考察無外乎以上三種類型。死鎖的概念和死鎖的四個產生條件是固定的,上文都已經講到。但解決方案並不止以上列出的四種。
事實上,使用預先拷貝資源解決死鎖問題的方案一般並不常用。這是由於拷貝的成本往往很大,並且影響效率。實際工作中較常採用的是第三種方案,通過控制加鎖順序解決死鎖:
  • 加鎖順序:當多個線程需要相同的一些鎖,但是按照不同的順序加鎖,死鎖就很容易發生。如果能確保所有的線程都是按照相同的順序獲得鎖,那麼死鎖就不會發生。當然這種方式需要你事先知道所有可能會用到的鎖,然而總有些時候是無法預知的。

除此之外,我們還可以通過設置加鎖時限或添加死鎖檢測避免死鎖:
  • 加鎖時限:加上一個超時時間,若一個線程沒有在給定的時限內成功獲得所有需要的鎖,則會進行回退並釋放所有已經獲得的鎖,然後等待一段隨機的時間再重試。但是如果有非常多的線程同一時間去競爭同一批資源,就算有超時和回退機制,還是可能會導致這些線程重複地嘗試但卻始終得不到鎖。

  • 死鎖檢測:死鎖檢測即每當一個線程獲得了鎖,會在線程和鎖相關的數據結構中( map 、 graph 等)將其記下。除此之外,每當有線程請求鎖,也需要記錄在這個數據結構中。死鎖檢測是一個更好的死鎖預防機制,它主要是針對那些不可能實現按序加鎖並且鎖超時也不可行的場景。

其中,死鎖檢測最出名的算法是由艾茲格·迪傑斯特拉在 1965 年設計的銀行家算法,通過記錄系統中的資源向量、最大需求矩陣、分配矩陣、需求矩陣,以保證系統只在安全狀態下進行資源分配,由此來避免死鎖,對於面算法崗的同學一定要對其有所瞭解。

OK,以上就是大廠面試過程中可能會遇到的一些死鎖相關知識,大家可以多多思考,記住死鎖的四個條件,然後去引申,面試就不成問題。


後臺回覆“加羣”,帶你進入高手如雲交流羣。如果你喜歡本文,不妨分享給你喜歡的朋友讀,或者點下右下角的“在看”,謝謝了!

推薦閱讀:


喜歡,就給我一個“在看”


10T 技術資源大放送!包括但不限於:雲計算、虛擬化、微服務、大數據、網絡、Linux、Docker、Kubernetes、Python、Go、C/C++、Shell、PPT 等。在公衆號內回覆「1024」,即可免費獲取!!

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