上一節講了關鍵字synchronized的基本用法。這一節我將用一個實際的例子來深入多線程同步機制的理解。
存取款改造
/**
* Created by qianyi on 2017/9/9.
*/
public class FetchOperationMoney {
public static void main(String[] args) {
Bank bank = new Bank();
Thread t1 = new AtmSaveMoney(bank);//ATM存錢
Thread t2 = new GuitaiSaveMoney(bank);//櫃檯存錢
Thread t3 = new AtmFetchMoney(bank);//ATM取錢
Thread t4 = new GuiTaiFetchMoney(bank);//櫃檯取錢
//分別啓動四個線程去對賬戶進行操作
bank.searchBalance();//訪問沒有加synchronized的方法,
t1.start();
bank.searchBalance();//訪問沒有加synchronized的方法,
t2.start();
bank.searchBalance();//訪問沒有加synchronized的方法,
t3.start();
bank.searchBalance();//訪問沒有加synchronized的方法,
t4.start();
bank.searchBalance();//訪問沒有加synchronized的方法,
}
}
/**
* 銀行賬戶
*/
class Bank {
/**
* 賬戶餘額
*/
private int balance = 1000;
/**
* 操作取錢的方法
*
* @param number 取錢金額
* @return
*/
public synchronized int fetchMoney(int number, String channel) {
System.out.println("取款開始:");
System.out.println("取款來自渠道:" + channel + ",賬戶操作前餘額:" + balance);
System.out.println("賬戶取款金額:" + number);
if (number < 0) {
return -1;
} else if (number > balance) {
return -2;
} else if (balance < 0) {
return -3;
} else {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
balance -= number;
System.out.println("balance is:" + balance);
System.out.println("------------------------------------------------");
return number;
}
}
/**
* 存錢
*
* @param number 存錢金額
* @return
*/
public synchronized int saveMoney(int number, String channel) {
System.out.println("存款開始:");
System.out.println("存款款來自渠道:" + channel + ",賬戶操作前餘額:" + balance);
System.out.println("賬戶存款金額:" + number);
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
balance += number;
System.out.println("balance is:" + balance);
System.out.println("------------------------------------------------");
return number;
}
/**
* 查詢餘額,沒有關鍵字synchronized
*/
public void searchBalance() {
System.out.println("*****************************************");
System.out.println("查詢----賬戶餘額爲:" + balance);
System.out.println("*****************************************");
}
}
/**
* ATM取錢
*/
class AtmFetchMoney extends Thread {
private Bank bank;
public AtmFetchMoney(Bank bank) {
this.bank = bank;
}
@Override
public void run() {
bank.fetchMoney(800, "ATM");
}
}
/**
* 櫃檯取錢
*/
class GuiTaiFetchMoney extends Thread {
private Bank bank;
public GuiTaiFetchMoney(Bank bank) {
this.bank = bank;
}
@Override
public void run() {
bank.fetchMoney(800, "櫃檯");
}
}
/**
* 櫃檯存錢
*/
class GuitaiSaveMoney extends Thread {
private Bank bank;
public GuitaiSaveMoney(Bank bank) {
this.bank = bank;
}
@Override
public void run() {
bank.saveMoney(800, "櫃檯");
}
}
/**
* ATM存錢
*/
class AtmSaveMoney extends Thread {
private Bank bank;
public AtmSaveMoney(Bank bank) {
this.bank = bank;
}
@Override
public void run() {
bank.saveMoney(800, "ATM");
}
}
首先上面代碼:
1.賬戶初始化餘額1000
2.Bank有兩個方法,分別是取錢和存錢的操作,取錢和存錢的操作分別傳入了金額和渠道(ATM 櫃檯),都使用了synchronized關鍵字。
3.四個多線程。ATM取錢、ATM存錢、櫃檯存錢、櫃檯取錢,對其存錢和取錢的操作。
4.增加一個查詢餘額searchBalance()的方法,查詢餘額,沒有關鍵字synchronized.
5. 模擬存錢需要等待10S,即 Thread.sleep(10000);
6.main方法中,我們多次調用查詢餘額方法,然後啓動多個線程去操作同一個賬戶。分別通過ATM和櫃檯去存800和取款800。
如果餘額依然是1000,那麼說明多線程同步機制起到了實質的作用。
結果:
*****************************************
查詢----賬戶餘額爲:1000
*****************************************
*****************************************
查詢----賬戶餘額爲:1000
*****************************************
存款開始:
*****************************************
查詢----賬戶餘額爲:1000
*****************************************
存款款來自渠道:ATM,賬戶操作前餘額:1000
賬戶存款金額:800
*****************************************
查詢----賬戶餘額爲:1000
*****************************************
*****************************************
查詢----賬戶餘額爲:1000
*****************************************
balance is:1800
------------------------------------------------
取款開始:
取款來自渠道:櫃檯,賬戶操作前餘額:1800
賬戶取款金額:800
balance is:1000
------------------------------------------------
取款開始:
取款來自渠道:ATM,賬戶操作前餘額:1000
賬戶取款金額:800
balance is:200
------------------------------------------------
存款開始:
存款款來自渠道:櫃檯,賬戶操作前餘額:200
賬戶存款金額:800
balance is:1000
------------------------------------------------
總結如下:
通過上面的例子可以發現,沒有加synchronized關鍵字的方法searchBalance()不受同步機制的影響。其餘的方法存款取款依次操作。不會出現競爭統一資源導致數據不對。只有當第一個線程將對象鎖釋放之後,後面纔有執行。
上面代碼可以直接copy到本地執行