日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
Java高并發(fā)詳解,死鎖的成因與解決方法

1 死鎖成因

死鎖是在多線程或多進(jìn)程環(huán)境中一種特定的并發(fā)問(wèn)題。當(dāng)兩個(gè)或多個(gè)線程(或進(jìn)程)相互等待對(duì)方所持有的資源時(shí),就會(huì)發(fā)生死鎖,導(dǎo)致系統(tǒng)無(wú)法繼續(xù)執(zhí)行。就是說(shuō),死鎖是由于相互等待對(duì)方所持有的資源而導(dǎo)致的一種僵局。在這種狀態(tài)下,系統(tǒng)無(wú)法繼續(xù)進(jìn)行,不能取得任何進(jìn)展。

創(chuàng)新互聯(lián)公司-專業(yè)網(wǎng)站定制、快速模板網(wǎng)站建設(shè)、高性價(jià)比莆田網(wǎng)站開(kāi)發(fā)、企業(yè)建站全套包干低至880元,成熟完善的模板庫(kù),直接使用。一站式莆田網(wǎng)站制作公司更省心,省錢,快速模板網(wǎng)站建設(shè)找我們,業(yè)務(wù)覆蓋莆田地區(qū)。費(fèi)用合理售后完善,十余年實(shí)體公司更值得信賴。

在Java中,synchronized關(guān)鍵字用于創(chuàng)建線程安全的類或方法,確保同步方法或同步塊在同一時(shí)間只能由一個(gè)線程訪問(wèn),從而防止多線程環(huán)境中的數(shù)據(jù)損壞和競(jìng)態(tài)條件的發(fā)生。當(dāng)一個(gè)線程正在執(zhí)行同步代碼時(shí),其他線程必須等待,直到當(dāng)前線程釋放鎖,才能訪問(wèn)同步資源。這種機(jī)制確保了線程之間的順序執(zhí)行,可避免數(shù)據(jù)不一致的問(wèn)題。

圖片

圖片

然而,在使用synchronized關(guān)鍵字時(shí)需小心謹(jǐn)慎,因?yàn)榭赡軐?dǎo)致死鎖的問(wèn)題。當(dāng)多個(gè)線程以不同的順序請(qǐng)求相同的鎖時(shí),可能會(huì)發(fā)生死鎖。例如,線程A持有鎖A并等待鎖B,而線程B持有鎖B并等待鎖A,它們會(huì)相互等待對(duì)方釋放鎖,導(dǎo)致程序無(wú)法繼續(xù)執(zhí)行。

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

例如,假設(shè)有兩個(gè)線程,

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

以下是代碼示例:

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("轉(zhuǎn)賬線程1:獲取鎖1。");
                try { Thread.sleep(100); } catch (InterruptedException e) {}
                System.out.println("轉(zhuǎn)賬線程1:等待鎖2...");
                synchronized (Lock2) {
                    System.out.println("轉(zhuǎn)賬線程1:獲取鎖1和鎖2。");
                    account1.transferTo(account2, 200);
                }
            }
        });

        Thread transferThread2 = new Thread(() -> {
            synchronized (Lock2) {
                System.out.println("轉(zhuǎn)賬線程2:獲取鎖2。");
                try { Thread.sleep(100); } catch (InterruptedException e) {}
                System.out.println("轉(zhuǎn)賬線程2:等待鎖1...");
                synchronized (Lock1) {
                    System.out.println("轉(zhuǎn)賬線程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 + " 從一個(gè)賬戶轉(zhuǎn)賬到另一個(gè)賬戶。");
        } else {
            System.out.println("轉(zhuǎn)賬余額不足。");
        }
    }

    public void deposit(int amount) {
        this.balance += amount;
    }
}

輸出結(jié)果:

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...

現(xiàn)在,對(duì)輸出進(jìn)行解析:

  • 轉(zhuǎn)賬線程1:已獲取鎖1。線程1開(kāi)始執(zhí)行并成功獲取鎖1。
  • 轉(zhuǎn)賬線程2:已獲取鎖2。線程2開(kāi)始執(zhí)行并成功獲取鎖2。

此時(shí),兩個(gè)線程都已經(jīng)各自獲取了一個(gè)鎖。然而,它們現(xiàn)在需要另一個(gè)鎖來(lái)完成交易,從而進(jìn)入等待階段:

  • 轉(zhuǎn)賬線程1:等待鎖2

線程1在持有鎖1的同時(shí),嘗試獲取鎖2以完成交易。然而,鎖2已被線程2獲取,因此線程1被迫等待鎖2釋放。

  • 轉(zhuǎn)賬線程2:等待鎖1

類似的,線程2在持有鎖2的同時(shí),嘗試獲取鎖1以完成交易。然而,鎖1已被線程1獲取,因此線程2被迫等待鎖1釋放。

此時(shí),兩個(gè)線程都處于等待狀態(tài),每個(gè)線程都在等待另一個(gè)線程釋放它所需的鎖。由于處于死鎖狀態(tài),因此兩個(gè)線程都無(wú)法繼續(xù)執(zhí)行。

2 預(yù)防死鎖

2.1 鎖的順序

鎖的順序是一種簡(jiǎn)單但有效的死鎖預(yù)防技術(shù)。它要求所有線程按照相同的順序獲取鎖。在示例中,有兩個(gè)銀行賬戶,并且多個(gè)線程代表這些賬戶之間的交易。為了避免死鎖,將定義一種一致的順序,以避免循環(huán)等待條件。下面是修改后的代碼:

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("轉(zhuǎn)賬線程1:已獲取鎖1。");
                try { Thread.sleep(100); } catch (InterruptedException e) {}
                System.out.println("轉(zhuǎn)賬線程1:等待鎖2...");
                synchronized (Lock2) {
                    System.out.println("轉(zhuǎn)賬線程1:已獲取鎖1和鎖2。");
                    account1.transferTo(account2, 200);
                }
            }
        });

        Thread transferThread2 = new Thread(() -> {
            synchronized (Lock1) {
                System.out.println("轉(zhuǎn)賬線程2:已獲取鎖1。");
                try { Thread.sleep(100); } catch (InterruptedException e) {}
                System.out.println("轉(zhuǎn)賬線程2:等待鎖2...");
                synchronized (Lock2) {
                    System.out.println("轉(zhuǎn)賬線程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.

這個(gè)銀行賬戶轉(zhuǎn)賬程序如何避免死鎖?

該銀行賬戶轉(zhuǎn)賬程序使用鎖來(lái)避免死鎖。兩個(gè)線程,轉(zhuǎn)賬線程1和轉(zhuǎn)賬線程2,都需要獲取鎖1和鎖2才能進(jìn)行轉(zhuǎn)賬。然而,它們以不同的順序獲取鎖。

轉(zhuǎn)賬線程1:

     (1)獲取鎖1。

     (2)等待鎖2。

     (3)獲取鎖2。 

     (4)從一個(gè)賬戶轉(zhuǎn)賬200美元到另一個(gè)賬戶。

     (5)釋放鎖2。

     (6)釋放鎖1。

轉(zhuǎn)賬線程2:

     (1)獲取鎖2。

     (2)等待鎖1。

     (3)獲取鎖1。

     (4)從一個(gè)賬戶轉(zhuǎn)賬100美元到另一個(gè)賬戶。

     (5)釋放鎖1。

     (6)釋放鎖2。

這兩個(gè)線程以不同的順序獲取鎖,但釋放鎖的順序與獲取鎖的相反順序相同。這樣可以避免死鎖。

在這種情況下避免死鎖的關(guān)鍵是,兩個(gè)線程按照相同的順序獲取鎖:首先是Lock1,然后是Lock2。鎖獲取順序的一致性確保了一個(gè)線程在另一個(gè)線程釋放鎖之后可以繼續(xù)執(zhí)行,避免了循環(huán)等待條件,從而使兩個(gè)交易都能成功完成。

2.2 使用超時(shí)機(jī)制

使用超時(shí)機(jī)制是預(yù)防死鎖的另一種方式。在獲取鎖時(shí),線程可以指定一個(gè)超時(shí)時(shí)間。如果在指定的時(shí)間內(nèi)無(wú)法獲取鎖,線程將放棄并稍后重試。

這在某些情況下很有用,例如線程正在等待一個(gè)被其他線程持有且無(wú)響應(yīng)或被阻塞的鎖。通過(guò)使用超時(shí)機(jī)制,線程可以避免進(jìn)入死鎖狀態(tài)。

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超時(shí)");
                }
            }
        });

        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 Lock1
Thread 2: Acquired Lock1
Thread 2: Acquired Lock2
Thread 1: Timed out waiting for Lock2

解釋:

在這個(gè)示例中,線程1在嘗試獲取鎖2時(shí)使用了超時(shí)機(jī)制。這意味著如果在指定的時(shí)間內(nèi)無(wú)法獲取鎖,它將打印一條消息并繼續(xù)執(zhí)行。

在這種情況下,線程1能夠獲取鎖1,但無(wú)法獲取鎖2。而線程2則能夠獲取兩個(gè)鎖。在線程2獲取鎖2之后,線程1超時(shí)并打印一條消息。


網(wǎng)站題目:Java高并發(fā)詳解,死鎖的成因與解決方法
分享網(wǎng)址:http://www.dlmjj.cn/article/djphjsg.html