多線程同步機制2-深入理解synchronized關鍵字

上一節講了關鍵字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到本地執行

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章