銀行取款[多執行緒]{使用同步方法確保執行緒同步}
經典例子:老婆(朱麗葉)老公(羅密歐),使用銀行卡和存摺,或者網銀等,同時對同一賬戶操作的安全問題。
此處用多執行緒實現,同時取款的模擬實現,使用同步方法確保執行緒同步,檢視取款安全隱患問題,程式碼如下:
-------------------------------------------------------------------------------------------------------------------------------------------
* 執行緒同步 :使用同步方法,實現執行緒同步
* 同步synchronized方法的物件監視鎖為this,當前物件
* 多個執行緒使用同一把鎖,如果執行緒安全必需確保:多個執行緒使用的是同一個this物件(Runnable適用於共享同一物件[如:this],如果Thread繼承就會有問題[推薦使用 Runnable])
* 所有訪問此物件方法的執行緒都在方法外等待,都會判斷同步鎖,降低效率,但確保執行緒安全問題
* java的每個物件都有一個內建鎖,當用synchronized關鍵字修飾方法時,內建鎖會保護整個方法。在呼叫該方法前,執行緒需要獲得內建鎖,否則就處於阻塞狀
*synchronized關鍵字也可以修飾靜態方法,此時如果呼叫該靜態方法,將會鎖住整個類
----------------------------------------------------------------------------------------------------------
private synchronized void makeWithdraw(int amount){...}
銀行賬戶:
package com.tsxs.bank; public class BankAccount { //餘額 private int balance = 500; //查詢 public int getBalance(){ return banlance; } //取款 public void withdraw(int amount){ banlance = banlance - amount; } //存款 public void deposit(int amount){ banlance = banlance + amount; } }
同步方法:
package com.tsxs.syncmethods; import com.tsxs.bank.BankAccount; /** * 此執行緒類實現Runnable介面<br /> * 執行緒同步 :使用同步方法,實現執行緒同步<br /> * 同步synchronized方法的的物件監視鎖為this,當前物件<br /> * 多個執行緒使用同一把鎖,如果執行緒安全必需確保:多個執行緒使用的是同一個this物件<br /> * 所有訪問此物件方法的執行緒都在方法外等待,都會判斷同步鎖,降低效率,但確保執行緒安全問題<br /> * */ public class SyncMethod implements Runnable{ //所有Thread多執行緒執行緒都共享Runnable(介面物件)和account物件 private BankAccount account = new BankAccount(); @Override public void run() { for(int i = 0; i< 5; i++){ //總共取款5次 makeWithdraw(100); //每次取款100 if(account.getBalance() < 0){ System.out.println("☆"+Thread.currentThread().getName()+" 透支了!"); } } } /** * makeWithdraw 賬戶取款 * @param amount 取款金額<br /> * 列印log記錄取款過程 * */ private synchronized void makeWithdraw(int amount){ if(account.getBalance() >= amount){ //如果餘額足夠則取款 System.out.println("☆"+Thread.currentThread().getName()+" 準備取款!"); try { Thread.sleep(500); } catch (InterruptedException e) { System.out.println(Thread.currentThread().getName()+" 準備取款,等待0.5s執行緒中斷!"+e.getMessage()); } account.withdraw(amount); System.out.println("☆"+Thread.currentThread().getName()+" 完成"+amount+"取款!餘額為"<span>+account.getBalance()); </span> }else{ //餘額不足則提示 System.out.println("☆"+"餘額不足以支付"+Thread.currentThread().getName()+amount+" 的取款,餘額為"+account.getBalance()); } } }
測試程式碼:
package com.tsxs.test;
import org.junit.Test;
import com.tsxs.syncmethods.NoSync;
import com.tsxs.syncmethods.SyncMethod;
public class TreadSyncTest {
// @Test
// public void test() {
/*Junit不適合多執行緒併發測試。
因為執行緒還在啟用狀態的時候,Junit已經執行完成。
在Junit的TestRunner中,它沒有被設計成搜尋Runnable例項,
並且等待這些執行緒發出報告,它只是執行它們並且忽略了它們的存在。
綜上,不可能在Junit中編寫和維護多執行緒的單元測試。
}*/
public static void main(String[] args) {
//實現Runnable:所有Thread多執行緒執行緒都共享Runnable(介面物件)
// NoSync target =new NoSync();
SyncMethod target = new SyncMethod();
//建立李琦和他老婆兩個執行緒實現取款(同時)
Thread lq = new Thread(target);
lq.setName("羅密歐");
Thread lqwf = new Thread(target);
lqwf.setName("朱麗葉");
//呼叫Thread物件的start()方法,啟動執行緒,執行run()方法(OS)
lq.start();
lqwf.start();
}
}
測試結果:☆羅密歐 準備取款!
☆羅密歐 完成100取款!餘額為400
☆羅密歐 準備取款!
☆羅密歐 完成100取款!餘額為300
☆羅密歐 準備取款!
☆羅密歐 完成100取款!餘額為200
☆朱麗葉 準備取款!
☆朱麗葉 完成100取款!餘額為100
☆羅密歐 準備取款!
☆羅密歐 完成100取款!餘額為0
☆餘額不足以支付羅密歐100 的取款,餘額為0
☆餘額不足以支付朱麗葉100 的取款,餘額為0
☆餘額不足以支付朱麗葉100 的取款,餘額為0
☆餘額不足以支付朱麗葉100 的取款,餘額為0
☆餘額不足以支付朱麗葉100 的取款,餘額為0
分析結果:
雙執行緒總共取款10次,賬戶總額為500.
取款結果:在多執行緒訪問下,成功取款總額為500,並且其他取款下,正確提示資訊。
多執行緒訪問安全保證!