日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不

當前位置:首頁 > 科技  > 軟件

Java高并發詳解,死鎖的成因與解決方法

來源: 責編: 時間:2023-12-09 15:22:13 296觀看
導讀1 死鎖成因死鎖是在多線程或多進程環境中一種特定的并發問題。當兩個或多個線程(或進程)相互等待對方所持有的資源時,就會發生死鎖,導致系統無法繼續執行。就是說,死鎖是由于相互等待對方所持有的資源而導致的一種僵局。在

1 死鎖成因

死鎖是在多線程或多進程環境中一種特定的并發問題。當兩個或多個線程(或進程)相互等待對方所持有的資源時,就會發生死鎖,導致系統無法繼續執行。就是說,死鎖是由于相互等待對方所持有的資源而導致的一種僵局。在這種狀態下,系統無法繼續進行,不能取得任何進展。NNR28資訊網——每日最新資訊28at.com

在Java中,synchronized關鍵字用于創建線程安全的類或方法,確保同步方法或同步塊在同一時間只能由一個線程訪問,從而防止多線程環境中的數據損壞和競態條件的發生。當一個線程正在執行同步代碼時,其他線程必須等待,直到當前線程釋放鎖,才能訪問同步資源。這種機制確保了線程之間的順序執行,可避免數據不一致的問題。NNR28資訊網——每日最新資訊28at.com

圖片圖片NNR28資訊網——每日最新資訊28at.com

圖片圖片NNR28資訊網——每日最新資訊28at.com

然而,在使用synchronized關鍵字時需小心謹慎,因為可能導致死鎖的問題。當多個線程以不同的順序請求相同的鎖時,可能會發生死鎖。例如,線程A持有鎖A并等待鎖B,而線程B持有鎖B并等待鎖A,它們會相互等待對方釋放鎖,導致程序無法繼續執行。NNR28資訊網——每日最新資訊28at.com

Thread A:   Lock Resource 1             Wait for Resource 2Thread B:   Lock Resource 2             Wait for Resource 1
  • 線程A:鎖定資源1,等待資源2
  • 線程B:鎖定資源2,等待資源1

例如,假設有兩個線程,NNR28資訊網——每日最新資訊28at.com

  • BankTransferExample表示了兩個線程(transferThread1和transferThread2)在兩個銀行賬戶之間轉賬的場景,鎖/資源(account1和account2)代表了與賬戶1和賬戶2相關聯的鎖。
  • Lock1和Lock2分別對應于與account1和account2相關聯的鎖。
  • 每個轉賬線程首先獲取一個賬戶的鎖,在等待一段時間以模擬工作后,再獲取另一個賬戶的鎖,然后執行轉賬操作。
  • BankAccount是一個簡單的表示銀行賬戶的類,具有轉賬和存款資金的方法。

以下是代碼示例:NNR28資訊網——每日最新資訊28at.com

public class BankTransferExample {    public static final Object Lock1 = new Object();    public static final Object Lock2 = new Object();    public static void main(String[] args) {        BankAccount account1 = new BankAccount(1000);        BankAccount account2 = new BankAccount(1500);        Thread transferThread1 = new Thread(() -> {            synchronized (Lock1) {                System.out.println("轉賬線程1:獲取鎖1。");                try { Thread.sleep(100); } catch (InterruptedException e) {}                System.out.println("轉賬線程1:等待鎖2...");                synchronized (Lock2) {                    System.out.println("轉賬線程1:獲取鎖1和鎖2。");                    account1.transferTo(account2, 200);                }            }        });        Thread transferThread2 = new Thread(() -> {            synchronized (Lock2) {                System.out.println("轉賬線程2:獲取鎖2。");                try { Thread.sleep(100); } catch (InterruptedException e) {}                System.out.println("轉賬線程2:等待鎖1...");                synchronized (Lock1) {                    System.out.println("轉賬線程2:獲取鎖1和鎖2。");                    account2.transferTo(account1, 100);                }            }        });        transferThread1.start();        transferThread2.start();    }}class BankAccount {    private int balance;    public BankAccount(int initialBalance) {        this.balance = initialBalance;    }    public void transferTo(BankAccount targetAccount, int amount) {        if (this.balance >= amount) {            this.balance -= amount;            targetAccount.deposit(amount);            System.out.println("將 $" + amount + " 從一個賬戶轉賬到另一個賬戶。");        } else {            System.out.println("轉賬余額不足。");        }    }    public void deposit(int amount) {        this.balance += amount;    }}

輸出結果:NNR28資訊網——每日最新資訊28at.com

Transfer Thread 1: Lock 1 acquired.Transfer Thread 2: Lock 2 acquired.Transfer Thread 1: Waiting for Lock 2...Transfer Thread 2: Waiting for Lock 1...

現在,對輸出進行解析:NNR28資訊網——每日最新資訊28at.com

  • 轉賬線程1:已獲取鎖1。線程1開始執行并成功獲取鎖1。
  • 轉賬線程2:已獲取鎖2。線程2開始執行并成功獲取鎖2。

此時,兩個線程都已經各自獲取了一個鎖。然而,它們現在需要另一個鎖來完成交易,從而進入等待階段:NNR28資訊網——每日最新資訊28at.com

  • 轉賬線程1:等待鎖2

線程1在持有鎖1的同時,嘗試獲取鎖2以完成交易。然而,鎖2已被線程2獲取,因此線程1被迫等待鎖2釋放。NNR28資訊網——每日最新資訊28at.com

  • 轉賬線程2:等待鎖1

類似的,線程2在持有鎖2的同時,嘗試獲取鎖1以完成交易。然而,鎖1已被線程1獲取,因此線程2被迫等待鎖1釋放。NNR28資訊網——每日最新資訊28at.com

此時,兩個線程都處于等待狀態,每個線程都在等待另一個線程釋放它所需的鎖。由于處于死鎖狀態,因此兩個線程都無法繼續執行。NNR28資訊網——每日最新資訊28at.com

2 預防死鎖

2.1 鎖的順序

鎖的順序是一種簡單但有效的死鎖預防技術。它要求所有線程按照相同的順序獲取鎖。在示例中,有兩個銀行賬戶,并且多個線程代表這些賬戶之間的交易。為了避免死鎖,將定義一種一致的順序,以避免循環等待條件。下面是修改后的代碼:NNR28資訊網——每日最新資訊28at.com

public class BankTransferExample {    public static final Object Lock1 = new Object();    public static final Object Lock2 = new Object();    public static void main(String[] args) {        BankAccount account1 = new BankAccount(1000);        BankAccount account2 = new BankAccount(1500);        Thread transferThread1 = new Thread(() -> {            synchronized (Lock1) {                System.out.println("轉賬線程1:已獲取鎖1。");                try { Thread.sleep(100); } catch (InterruptedException e) {}                System.out.println("轉賬線程1:等待鎖2...");                synchronized (Lock2) {                    System.out.println("轉賬線程1:已獲取鎖1和鎖2。");                    account1.transferTo(account2, 200);                }            }        });        Thread transferThread2 = new Thread(() -> {            synchronized (Lock1) {                System.out.println("轉賬線程2:已獲取鎖1。");                try { Thread.sleep(100); } catch (InterruptedException e) {}                System.out.println("轉賬線程2:等待鎖2...");                synchronized (Lock2) {                    System.out.println("轉賬線程2:已獲取鎖1和鎖2。");                    account2.transferTo(account1, 100);                }            }        });        transferThread1.start();        transferThread2.start();    }}
Transfer Thread 1: Lock 1 acquired.Transfer Thread 1: Waiting for Lock 2...Transfer Thread 1: Lock 1 & Lock 2 acquired.Transferred $200 from one account to another.Transfer Thread 2: Lock 2 acquired.Transfer Thread 2: Waiting for Lock 1...Transfer Thread 2: Lock 1 & Lock 2 acquired.Transferred $100 from one account to another.

這個銀行賬戶轉賬程序如何避免死鎖?NNR28資訊網——每日最新資訊28at.com

該銀行賬戶轉賬程序使用鎖來避免死鎖。兩個線程,轉賬線程1和轉賬線程2,都需要獲取鎖1和鎖2才能進行轉賬。然而,它們以不同的順序獲取鎖。NNR28資訊網——每日最新資訊28at.com

轉賬線程1:NNR28資訊網——每日最新資訊28at.com

     (1)獲取鎖1。NNR28資訊網——每日最新資訊28at.com

     (2)等待鎖2。NNR28資訊網——每日最新資訊28at.com

     (3)獲取鎖2。 NNR28資訊網——每日最新資訊28at.com

     (4)從一個賬戶轉賬200美元到另一個賬戶。NNR28資訊網——每日最新資訊28at.com

     (5)釋放鎖2。NNR28資訊網——每日最新資訊28at.com

     (6)釋放鎖1。NNR28資訊網——每日最新資訊28at.com

轉賬線程2:NNR28資訊網——每日最新資訊28at.com

     (1)獲取鎖2。NNR28資訊網——每日最新資訊28at.com

     (2)等待鎖1。NNR28資訊網——每日最新資訊28at.com

     (3)獲取鎖1。NNR28資訊網——每日最新資訊28at.com

     (4)從一個賬戶轉賬100美元到另一個賬戶。NNR28資訊網——每日最新資訊28at.com

     (5)釋放鎖1。NNR28資訊網——每日最新資訊28at.com

     (6)釋放鎖2。NNR28資訊網——每日最新資訊28at.com

這兩個線程以不同的順序獲取鎖,但釋放鎖的順序與獲取鎖的相反順序相同。這樣可以避免死鎖。NNR28資訊網——每日最新資訊28at.com

在這種情況下避免死鎖的關鍵是,兩個線程按照相同的順序獲取鎖:首先是Lock1,然后是Lock2。鎖獲取順序的一致性確保了一個線程在另一個線程釋放鎖之后可以繼續執行,避免了循環等待條件,從而使兩個交易都能成功完成。NNR28資訊網——每日最新資訊28at.com

2.2 使用超時機制

使用超時機制是預防死鎖的另一種方式。在獲取鎖時,線程可以指定一個超時時間。如果在指定的時間內無法獲取鎖,線程將放棄并稍后重試。NNR28資訊網——每日最新資訊28at.com

這在某些情況下很有用,例如線程正在等待一個被其他線程持有且無響應或被阻塞的鎖。通過使用超時機制,線程可以避免進入死鎖狀態。NNR28資訊網——每日最新資訊28at.com

public class LockTimeoutExample {    public static final Object Lock1 = new Object();    public static final Object Lock2 = new Object();    public static void main(String[] args) {        Thread thread1 = new Thread(() -> {            synchronized (Lock1) {                System.out.println("線程1:已獲取鎖1");                try { Thread.sleep(100); } catch (InterruptedException e) {}                if (synchronized (Lock2, 1000)) {                    System.out.println("線程1:已獲取鎖2");                } else {                    System.out.println("線程1:等待鎖2超時");                }            }        });        Thread thread2 = new Thread(() -> {            synchronized (Lock1) {                System.out.println("線程2:已獲取鎖1");                try { Thread.sleep(100); } catch (InterruptedException e) {}                synchronized (Lock2) {                    System.out.println("線程2:已獲取鎖2");                }            }        });        thread1.start();        thread2.start();    }}
Thread 1: Acquired Lock1Thread 2: Acquired Lock1Thread 2: Acquired Lock2Thread 1: Timed out waiting for Lock2

解釋:NNR28資訊網——每日最新資訊28at.com

在這個示例中,線程1在嘗試獲取鎖2時使用了超時機制。這意味著如果在指定的時間內無法獲取鎖,它將打印一條消息并繼續執行。NNR28資訊網——每日最新資訊28at.com

在這種情況下,線程1能夠獲取鎖1,但無法獲取鎖2。而線程2則能夠獲取兩個鎖。在線程2獲取鎖2之后,線程1超時并打印一條消息。NNR28資訊網——每日最新資訊28at.com

本文鏈接:http://www.www897cc.com/showinfo-26-40656-0.htmlJava高并發詳解,死鎖的成因與解決方法

聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com

上一篇: 文本抓取利器,Python和Beautiful Soup爬蟲助你事半功倍

下一篇: 深入了解Git LFS:高效管理大型文件的利器

標簽:
  • 熱門焦點
Top 主站蜘蛛池模板: 巴彦淖尔市| 且末县| 鹤山市| 阳城县| 东阿县| 建水县| 松潘县| 怀柔区| 博爱县| 胶南市| 丰台区| 鸡西市| 阳新县| 黄石市| 上蔡县| 荆州市| 叶城县| 如皋市| 炎陵县| 西昌市| 攀枝花市| 长沙县| 克东县| 多伦县| 门源| 上饶县| 库车县| 梁平县| 九龙坡区| 铁岭县| 莱州市| 寿光市| 个旧市| 丘北县| 永嘉县| 固安县| 浑源县| 鲁山县| 思南县| 林西县| 鹤壁市|