Java多執行緒 同步(一)

2021-06-28 09:32:42 字數 4115 閱讀 3080

可能我們在開發專案進行過程中,通常會冒出這樣的困惑:應該選擇效率,還是選擇質量?會不會有偷懶的思維,覺得把一些摸不清頭緒,不知道怎麼寫的**片段去掉,可以節省許多時間,更早的完成專案計畫,其實以前我也是這麼想的,但最近我開始意識到,這個問題的糾結之處不在於選擇困難,而在於問題本身是個偽命題。

什麼是「質量」呢?對於我們程式設計師來說可能是測試通過率,變數命名,**格式化,元件化,查詢bug,程式測試等。也有可能是程式的可擴充套件性,服務延時,產品功能的完整程度。前一種圍繞**的問題可以看成「**質量」問題,第二種可以看成「執行質量」。

從「**質量」來看,走捷徑的偷懶思維,其實是種十分短視的做法。含糊繞過某個問題,你可能一時覺得省事不少,但到頭來,往往發現因此攪亂了系統而要花費更多的時間來一行行的檢查**,找出bug,甚至重新調整整理邏輯框架。所以犧牲**質量換取速度通常是得不償失的。

相反地,高質量的**其實是可以幫助你節省時間的。統一**規範和變數命名,不僅可以幫到別的程式設計師,還可以幫到以後的你,更好地理解你現在寫下的**,經過嚴密思考而設計出的輕量級**架構,則可以讓你在迭代產品的時候獲得更高的效率,更清晰地了解該從何入手,而不是到資料庫裡找需要替代的地方,而高測試通過率還可以給你充足的自信去調整**,減少bug數量,減少qa時間。

我們現階段還是注重好**的質量,執行質量就不多說啦,當然這些都是題外話,想和各位分享!接下來還是進入正題。

在大多數實際的多執行緒應用中,兩個或兩個以上的執行緒需要共享對同一資料的訪問。如果兩個執行緒訪問相同的物件,並且每乙個執行緒都呼叫了修改該物件的方法,將會發生什麼?可能就會產生錯誤的物件。這樣的情況通常稱為競爭條件。

我們來模擬乙個有若干賬戶的銀行,隨機在這些賬戶之間轉賬,每乙個賬戶乙個執行緒。每一次交易,會從執行緒所服務的賬戶中隨機轉移一定的金額到另乙個賬戶中。

這是bank類中轉賬的方法:

public void transfer(int from, int to, double amount) 

system.out.print(thread.currentthread());

accounts[from] -= amount;

system.out.printf("轉賬金額: %10.2f 轉出賬戶: %d 轉入賬戶: %d", amount, from, to);

accounts[to] += amount;

system.out.printf(" 最後的金額: %10.2f%n", gettotalbalance());

}

這是轉賬執行緒類中run方法(隨機選擇乙個目標賬戶和乙個隨機賬戶呼叫轉賬方法,然後睡眠):

@override

public void run() catch (interruptedexception e) }}

當它執行時,我們不清楚某一時間某乙個賬戶有多少錢,但是,所有賬戶的總金額應該是保持不變的。

下面是例子的完整**:

bank類:

/**

* @author xzzhao

*/public class bank

}public void transfer(int from, int to, double amount)

system.out.print(thread.currentthread());

accounts[from] -= amount;

system.out.printf("轉賬金額: %10.2f 轉出賬戶: %d 轉入賬戶: %d", amount, from, to);

accounts[to] += amount;

system.out.printf(" 最後的金額: %10.2f%n", gettotalbalance());

}public double gettotalbalance()

return sum;

}public int size()

}

transferrunnable類:

/**

* @author xzzhao

*/public class transferrunnable implements runnable

@override

public void run() catch (interruptedexception e) }}

}

test:

/**

* @author xzzhao

*/public class unsynchbanktest }}

程式出錯:總金額發生變化

有兩種機制防止**塊受併發訪問的干擾。乙個是reentrantlock類,還有乙個是synchronized關鍵字。

用reentrantlock保護**塊的結構:

lock mylock = new reentrantlock();

mylock.lock();

try finally

這一結構確保任何時刻只有乙個執行緒進入。一旦乙個執行緒封鎖了鎖物件,其他任何執行緒都無法通過lock語句。當其他執行緒呼叫lock時,它們被阻塞,直到第乙個執行緒釋放鎖物件。

使用乙個鎖來保護bank類的transfer方法:

/**

* @author xzzhao

*/public class bank

banklock.lock();

try finally

}...

}

再次執行程式後,發現不會出現錯誤,執行緒之間不會互相影響了。

鎖是可重入的,因為執行緒可以重複地獲得已經持有的鎖。鎖保持乙個持有計數來跟蹤lock方法的巢狀呼叫。執行緒在每一次呼叫lock都要呼叫unlock來釋放鎖。所以,被乙個鎖保護的**可以呼叫另乙個使用相同的鎖的方法。

當執行緒進入區域,卻發現在某一條件滿足之後才能執行。要使用乙個條件物件來管理那些已經獲得了乙個鎖但是卻不能做有用工作的執行緒。

我們來修改一下例子,我們使用條件物件來避免選擇沒有足夠資金的賬戶作為轉出賬戶。

bank類:

/**

* @author xzzhao

*/public class bank

banklock = new reentrantlock();

sufficientfunds = banklock.newcondition(); // 獲取乙個和該鎖相關的條件物件

}public void transfer(int from, int to, double amount) throws interruptedexception

system.out.print(thread.currentthread());

accounts[from] -= amount;

system.out.printf("轉賬金額: %10.2f 轉出賬戶: %d 轉入賬戶: %d", amount, from, to);

accounts[to] += amount;

system.out.printf(" 最後的金額: %10.2f%n", gettotalbalance());

sufficientfunds.signalall(); // 解除該條件的等待集中的所有執行緒的阻塞狀態

} finally

}public double gettotalbalance()

return sum;

} finally

}public int size()

}

transferrunnable類:

/**

* @author xzzhao

*/public class transferrunnable implements runnable

@override

public void run()

} catch (interruptedexception e) }}

JAVA多執行緒同步

1.同步 塊 synchronized 物件 例程 package cn.wf.thread1 多執行緒同步 同步 塊 author wf public class thread test class thread01 implements runnable trycatch interrupted...

java多執行緒 同步

2019獨角獸企業重金招聘python工程師標準 synchronized關鍵字鎖的物件可以是方法 變數 類和當前例項。synchronized不能被繼承,子類將自動去除synchronized關鍵字 public synchronized void 變為public void 方法public s...

java多執行緒值執行緒同步

在多執行緒的操作中,多個執行緒有可能同時處理同一資源,這就是多執行緒的共享資料。如下程式 public class threaddemo catch interruptedexception e catch interruptedexception e catch interruptedexcept...