打破冷漠僵局文章_研究僵局–第1部分
打破冷漠僵局文章
我敢肯定我們都去過那里:太晚了,您餓了,您的服務(wù)器已掛起,或者您的應(yīng)用程序正在以蝸牛的速度運(yùn)行,并且有人喘著粗氣想要您解決問題,然后再去解決。 您的應(yīng)用程序意外掛起的可能原因之一是稱為死鎖的線程問題。 無需贅述,線程可以處于多種狀態(tài)之一,如下面的UML狀態(tài)圖所示……
…死鎖與BLOCKED狀態(tài)有關(guān),API文檔將其定義為“一個(gè)等待監(jiān)視器鎖定而被阻塞的線程”。
那么,什么是僵局? 簡而言之,在給定兩個(gè)線程A和B的情況下,當(dāng)線程A由于等待線程B釋放監(jiān)視器鎖定而阻塞時(shí),線程B因等待線程A釋放相同的監(jiān)視器鎖定而阻塞而發(fā)生死鎖。
但是,事情可能比這更復(fù)雜,因?yàn)樗梨i可能包含一堆線程。 例如,線程A因?yàn)檎诘却€程B而阻塞,線程B因?yàn)檎诘却€程C而阻塞,線程C因?yàn)檎诘却€程D而阻塞,所以線程D因?yàn)檎诘却鼸,E而阻塞,因?yàn)樗诘却鼺和F阻塞,因?yàn)樗诘却鼳。
訣竅是找出哪些線程被阻塞以及為什么被阻塞,這是通過從應(yīng)用程序中獲取線程轉(zhuǎn)儲來完成的。 線程轉(zhuǎn)儲只是快照報(bào)告,顯示給定時(shí)間點(diǎn)所有應(yīng)用程序線程的狀態(tài)。 有幾種工具和技術(shù)可以幫助您掌握線程轉(zhuǎn)儲,包括jVisualVM , jstack和unix kill命令。 但是,在獲取和解釋線程轉(zhuǎn)儲之前,我需要一些代碼來創(chuàng)建死鎖
我為此選擇的方案是簡單的銀行帳戶轉(zhuǎn)帳之一。 這個(gè)想法是,有一個(gè)余額轉(zhuǎn)移程序正在運(yùn)行,該程序使用一堆線程在不同帳戶之間隨機(jī)轉(zhuǎn)移各種金額。 在此程序中,使用以下非常簡單的Account類表示銀行帳戶:
public class Account {private final int number;private int balance;public Account(int number, int openingBalance) {this.number = number;this.balance = openingBalance;}public void withdraw(int amount) throws OverdrawnException {if (amount > balance) {throw new OverdrawnException();}balance -= amount;}public void deposit(int amount) {balance += amount;}public int getNumber() {return number;}public int getBalance() {return balance;} }上面的類為銀行帳戶建模,該銀行帳戶具有帳號和余額屬性,以及諸如deposit(...)和withdraw(...) 。 如果要提取的金額大于可用余額,則withdraw(...)將引發(fā)一個(gè)簡單的已檢查異常OverdrawnException 。
示例代碼中其余的類是DeadlockDemo及其嵌套類BadTransferOperation 。
public class DeadlockDemo {private static final int NUM_ACCOUNTS = 10;private static final int NUM_THREADS = 20;private static final int NUM_ITERATIONS = 100000;private static final int MAX_COLUMNS = 60;static final Random rnd = new Random();List<Account> accounts = new ArrayList<Account>();public static void main(String args[]) {DeadlockDemo demo = new DeadlockDemo();demo.setUp();demo.run();}void setUp() {for (int i = 0; i < NUM_ACCOUNTS; i++) {Account account = new Account(i, rnd.nextInt(1000));accounts.add(account);}}void run() {for (int i = 0; i < NUM_THREADS; i++) {new BadTransferOperation(i).start();}}class BadTransferOperation extends Thread {int threadNum;BadTransferOperation(int threadNum) {this.threadNum = threadNum;}@Overridepublic void run() {for (int i = 0; i < NUM_ITERATIONS; i++) {Account toAccount = accounts.get(rnd.nextInt(NUM_ACCOUNTS));Account fromAccount = accounts.get(rnd.nextInt(NUM_ACCOUNTS));int amount = rnd.nextInt(1000);if (!toAccount.equals(fromAccount)) {try {transfer(fromAccount, toAccount, amount);System.out.print(".");} catch (OverdrawnException e) {System.out.print("-");}printNewLine(i);}}// This will never get to here...System.out.println("Thread Complete: " + threadNum);}private void printNewLine(int columnNumber) {if (columnNumber % MAX_COLUMNS == 0) {System.out.print("\n");}}/*** The clue to spotting deadlocks is in the nested locking - synchronized keywords. Note that the locks DON'T* have to be next to each other to be nested.*/private void transfer(Account fromAccount, Account toAccount, int transferAmount) throws OverdrawnException {synchronized (fromAccount) {synchronized (toAccount) {fromAccount.withdraw(transferAmount);toAccount.deposit(transferAmount);}}}} }DeadlockDemo提供了創(chuàng)建DeadlockDemo的應(yīng)用程序框架。 它有兩個(gè)簡單的任務(wù): setup()和run() 。 setup()創(chuàng)建10個(gè)帳戶,并使用一個(gè)帳號和一個(gè)隨機(jī)的期初余額對其進(jìn)行初始化。 run()創(chuàng)建嵌套類BadTransferOperation 20個(gè)實(shí)例,該實(shí)例僅擴(kuò)展Thread并使它們開始運(yùn)行。 請注意,用于線程數(shù)和帳戶數(shù)的值完全是任意的。
BadTransferOperation是所有動(dòng)作發(fā)生的地方。 它的run()方法循環(huán)循環(huán)10000次,從accounts列表中隨機(jī)選擇兩個(gè)帳戶,并將0到1000之間的隨機(jī)數(shù)從一個(gè)accounts轉(zhuǎn)移到另一個(gè)accounts 。 如果fromAccount中的資金不足,則會(huì)引發(fā)異常,并在屏幕上顯示“-”。 如果一切順利,并且傳輸成功,則為“?!?。 在屏幕上打印。
事情的核心是包含FAULTY同步代碼的方法transfer(Account fromAccount, Account toAccount, int transferAmount) :
synchronized (fromAccount) {synchronized (toAccount) {fromAccount.withdraw(transferAmount);toAccount.deposit(transferAmount);}}此代碼首先鎖定fromAccount ,然后toAccount轉(zhuǎn)移現(xiàn)金,隨后釋放這兩個(gè)鎖定前。
給定兩個(gè)線程A和B以及帳戶1和2,那么當(dāng)線程A鎖定其編號為1的fromAccount并嘗試將其鎖定為帳戶2的toAccount ,將出現(xiàn)問題。同時(shí),線程B鎖定其fromAccount ,編號2和嘗試鎖定其toAccount ,即帳戶號1。因此,線程A在線程B上被toAccount ,線程B在線程A上被阻塞–死鎖。
如果運(yùn)行此應(yīng)用程序,則將獲得一些類似于以下內(nèi)容的輸出:
…隨著程序突然停止。
現(xiàn)在,我有一個(gè)死鎖的應(yīng)用程序,我的下一個(gè)博客實(shí)際上將掌握線程轉(zhuǎn)儲,并了解它的全部含義。
參考: Captain Debug's Blog博客中的調(diào)查死鎖–第1部分,來自我們的JCG合作伙伴 Roger Hughes。
翻譯自: https://www.javacodegeeks.com/2012/10/investigating-deadlocks-part-1.html
打破冷漠僵局文章
總結(jié)
以上是生活随笔為你收集整理的打破冷漠僵局文章_研究僵局–第1部分的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 以下哪项是DDOS攻击的整体特点(以下哪
- 下一篇: 一类医疗器械备案有效期是几年(一类医疗器