<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          面試官:手寫一個必然死鎖的例子

          共 4503字,需瀏覽 10分鐘

           ·

          2021-10-21 13:00

          程序員的成長之路
          互聯(lián)網(wǎng)/程序員/技術(shù)/資料共享?
          關(guān)注


          閱讀本文大概需要 5 分鐘。

          來自:blog.csdn.net/xiewenfeng520/article/details/107230996

          前言

          只對死鎖代碼感興趣的可以直接跳到第三小節(jié) 必然死鎖示例,如果對死鎖還不太了解的,我們可以一起來討論以下幾個議題
          • 什么是死鎖?
          • 死鎖有什么危害和特點?
          • 代碼實現(xiàn)一個必然死鎖的示例
          • 分析死鎖的過程
          推薦下自己做的 Spring Boot 的實戰(zhàn)項目:
          https://github.com/YunaiV/ruoyi-vue-pro

          項目環(huán)境

          • jdk 1.8

          • github 地址:https://github.com/huajiexiewenfeng/java-concurrent


            • 本章模塊:deadlock
          推薦下自己做的 Spring Cloud 的實戰(zhàn)項目:
          https://github.com/YunaiV/onemall

          1.什么是死鎖?

          關(guān)鍵詞:并發(fā)場景,多線程
          首先我們需要知道,死鎖一定發(fā)生在并發(fā)場景中。我們?yōu)榱吮WC線程安全,有時會給程序使用各種能保證并發(fā)安全的工具,尤其是鎖,但是如果在使用過程中處理不得當(dāng),就有可能會導(dǎo)致發(fā)生死鎖的情況。
          關(guān)鍵詞:互不相讓
          死鎖是一種狀態(tài),當(dāng)兩個(或多個)線程(或進程)相互持有對方所需要的資源,卻又都不主動釋放自己手中所持有的資源,導(dǎo)致大家都獲取不到自己想要的資源,所有相關(guān)的線程(或進程)都無法繼續(xù)往下執(zhí)行,在未改變這種狀態(tài)之前都不能向前推進,我們就把這種狀態(tài)稱為死鎖狀態(tài),認(rèn)為它們發(fā)生了死鎖。
          簡而言之,死鎖就是兩個或多個線程(或進程)被無限期地阻塞,相互等待對方手中資源的一種狀態(tài)。
          兩個線程死鎖的情況
          如圖所示,線程1 已經(jīng)持有了 鎖1,同時 線程2 也已經(jīng)持有了鎖2,然后 線程1 嘗試獲取 鎖2,但是 線程2 并沒有釋放 鎖2,所以 線程1 處于阻塞狀態(tài),同理可知,圖中的 線程2 獲取 鎖1也會被阻塞。
          這樣一來,線程1 和 線程2 就發(fā)生了死鎖,因為它們都相互持有對方想要的資源,卻又不釋放自己手中的資源,形成相互等待,而且會一直等待下去。

          2.死鎖的影響和危害

          2.1 死鎖的影響

          死鎖的影響在不同系統(tǒng)中是不一樣的,影響的大小一部分取決于當(dāng)前這個系統(tǒng)或者環(huán)境對死鎖的處理能力。
          2.1.1 數(shù)據(jù)庫中
          例如,在數(shù)據(jù)庫系統(tǒng)軟件的設(shè)計中,考慮了監(jiān)測死鎖以及從死鎖中恢復(fù)的情況。在執(zhí)行一個事務(wù)的時候可能需要獲取多把鎖,并一直持有這些鎖直到事務(wù)完成。在某個事務(wù)中持有的鎖可能在其他事務(wù)中也需要,因此在兩個事務(wù)之間有可能發(fā)生死鎖的情況,一旦發(fā)生了死鎖,如果沒有外部干涉,那么兩個事務(wù)就會永遠(yuǎn)的等待下去。
          但數(shù)據(jù)庫系統(tǒng)不會放任這種情況發(fā)生,當(dāng)數(shù)據(jù)庫檢測到這一組事務(wù)發(fā)生了死鎖時,根據(jù)策略的不同,可能會選擇放棄某一個事務(wù),被放棄的事務(wù)就會釋放掉它所持有的鎖,從而使其他的事務(wù)繼續(xù)順利進行。
          此時程序可以重新執(zhí)行被強行終止的事務(wù),而這個事務(wù)現(xiàn)在就可以順利執(zhí)行了,因為所有跟它競爭資源的事務(wù)都已經(jīng)在剛才執(zhí)行完畢,并且釋放資源了。
          2.1.2 JVM 中
          在 JVM 中,對于死鎖的處理能力就不如數(shù)據(jù)庫那么強大了。如果在 JVM 中發(fā)生了死鎖,JVM 并不會自動進行處理,所以一旦死鎖發(fā)生,就會陷入無窮的等待。

          2.2 死鎖的危害以及特點

          關(guān)鍵詞:概率性事件
          死鎖的問題和其他的并發(fā)安全問題一樣,是概率性的,也就是說,即使存在發(fā)生死鎖的可能性,也并不是 100% 會發(fā)生的。如果每個鎖的持有時間很短,那么發(fā)生沖突的概率就很低,所以死鎖發(fā)生的概率也很低。但是在線上系統(tǒng)里,可能每天有幾千萬次的“獲取鎖”、“釋放鎖”操作,在巨量的次數(shù)面前,整個系統(tǒng)發(fā)生問題的幾率就會被放大,只要有某幾次操作是有風(fēng)險的,就可能會導(dǎo)致死鎖的發(fā)生。
          也正是因為死鎖“不一定會發(fā)生”的特點,導(dǎo)致提前找出死鎖成為了一個難題。壓力測試雖然可以檢測出一部分可能發(fā)生死鎖的情況,但是并不足以完全模擬真實、長期運行的場景,因此沒有辦法把所有潛在可能發(fā)生死鎖的代碼都找出來。
          關(guān)鍵詞:危害大,發(fā)生幾率不高
          一旦發(fā)生了死鎖,根據(jù)發(fā)生死鎖的線程的職責(zé)不同,就可能會造成 子系統(tǒng)崩潰、性能降低 甚至 整個系統(tǒng)崩潰 等各種不良后果。而且死鎖往往發(fā)生在高并發(fā)、高負(fù)載的情況下,因為可能會直接影響到很多用戶,造成一系列的問題。以上就是死鎖發(fā)生幾率不高但是危害大的特點。

          3.必然死鎖示例

          public?class?MustDeadLockDemo?{

          ????public?static?void?main(String[]?args)?{
          ????????Object?lock1?=?new?Object();
          ????????Object?lock2?=?new?Object();
          ????????new?Thread(new?DeadLockTask(lock1,?lock2,?true),?"線程1").start();
          ????????new?Thread(new?DeadLockTask(lock1,?lock2,?false),?"線程2").start();

          ????}

          ????static?class?DeadLockTask?implements?Runnable?{

          ????????private?boolean?flag;
          ????????private?Object?lock1;
          ????????private?Object?lock2;

          ????????public?DeadLockTask(Object?lock1,?Object?lock2,?boolean?flag)?{
          ????????????this.lock1?=?lock1;
          ????????????this.lock2?=?lock2;
          ????????????this.flag?=?flag;
          ????????}

          ????????@Override
          ????????public?void?run()?{
          ????????????if?(flag)?{
          ????????????????synchronized?(lock1)?{
          ????????????????????System.out.println(Thread.currentThread().getName()?+?"->拿到鎖1");
          ????????????????????try?{
          ????????????????????????Thread.sleep(1000);
          ????????????????????}?catch?(InterruptedException?e)?{
          ????????????????????????e.printStackTrace();
          ????????????????????}
          ????????????????????System.out.println(Thread.currentThread().getName()?+?"->等待鎖2釋放...");
          ????????????????????synchronized?(lock2)?{
          ????????????????????????System.out.println(Thread.currentThread().getName()?+?"->拿到鎖2");
          ????????????????????}
          ????????????????}
          ????????????}
          ????????????if?(!flag)?{
          ????????????????synchronized?(lock2)?{
          ????????????????????System.out.println(Thread.currentThread().getName()?+?"->拿到鎖2");
          ????????????????????try?{
          ????????????????????????Thread.sleep(1000);
          ????????????????????}?catch?(InterruptedException?e)?{
          ????????????????????????e.printStackTrace();
          ????????????????????}
          ????????????????????System.out.println(Thread.currentThread().getName()?+?"->等待鎖1釋放...");
          ????????????????????synchronized?(lock1)?{
          ????????????????????????System.out.println(Thread.currentThread().getName()?+?"->拿到鎖1");
          ????????????????????}
          ????????????????}
          ????????????}
          ????????}
          ????}
          }

          執(zhí)行結(jié)果:
          可以看到程序一直處于阻塞狀態(tài)。

          4.過程分析

          其實上面的代碼示例發(fā)生死鎖的過程就是第一小節(jié)中 兩個線程發(fā)生死鎖 的情況,這里我們把圖拿過來,方便分析。
          本文使用 IDEA 進行調(diào)試,將斷點打在 33 行,run方法的第一行,選擇 Thread 模式。
          注意:調(diào)試過程,因為有人為的等待時間,所以并不會發(fā)生死鎖,這里只是演示線程執(zhí)行的順序和狀態(tài)。
          第一步,線程1進入,flag = true,進入第一個 synchronized 同步塊,拿到 lock1(鎖1)
          第二步,直接點擊 Resume Program(F9),進入線程2,此時 flag = false,進入第二個 synchronized 同步塊
          當(dāng)然如果 Thread.sleep 的時間夠長,或者操作速度夠快的話,也能發(fā)生死鎖。

          5.總結(jié)

          本章我們討論了什么是死鎖,以及死鎖的影響和危害,演示了一個必然死鎖的例子,然后使用 IDEA 工具調(diào)試了兩個線程發(fā)生死鎖的步驟。
          在 JVM 中如果發(fā)生死鎖,可能會導(dǎo)致程序部分甚至全部無法繼續(xù)向下執(zhí)行的情況,所以死鎖在 JVM 中所帶來的危害和影響是比較大的,我們需要盡量避免。
          最后如果在面試中碰到這一題,希望大家都能順利通過。

          推薦閱讀:

          在窮學(xué)生面前 “ 擺弄騷姿 ”,最美90后支教女老師翻車~!這TM就是所謂的 “ 支教媛 ” ~?

          面試官:你手寫過堵塞隊列嗎?我懵了。。

          最近面試BAT,整理一份面試資料《Java面試BATJ通關(guān)手冊》,覆蓋了Java核心技術(shù)、JVM、Java并發(fā)、SSM、微服務(wù)、數(shù)據(jù)庫、數(shù)據(jù)結(jié)構(gòu)等等。

          獲取方式:點個「在看」,點擊上方小卡片,進入公眾號后回復(fù)「面試題」領(lǐng)取,更多內(nèi)容陸續(xù)奉上。

          朕已閱?

          瀏覽 59
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  色婷婷aⅴ | 无码影音先锋 | 日日操网| 美女被干网站 | 黄色动漫在线免费观看 |