1. 程式人生 > >銀行取款[多執行緒]{使用同步方法確保執行緒同步}

銀行取款[多執行緒]{使用同步方法確保執行緒同步}

  經典例子:老婆(朱麗葉)老公(羅密歐),使用銀行卡和存摺,或者網銀等,同時對同一賬戶操作的安全問題。

 此處用多執行緒實現,同時取款的模擬實現,使用同步方法確保執行緒同步,檢視取款安全隱患問題,程式碼如下:

-------------------------------------------------------------------------------------------------------------------------------------------

 * 執行緒同步 :使用同步方法,實現執行緒同步
 * 同步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,並且其他取款下,正確提示資訊。

多執行緒訪問安全保證!