2015年8月4日 星期二

Java上課練習:多執行緒_同步化(synchronized)

同步化方法 ( synchronized )
多執行緒共享物件問題

進行多執行緒要注意同步的問題,當同時有多個執行緒進行時,若該程式需要某一執行緒執行完畢後,才能執行下一個執行緒,此時就需要進行同步化

製作一程式ATM,同時有三個執行緒進行提款
類別圖


程式碼
class Account {

    private int balance;//餘額

    //建構子
    public Account(int balance) {
        this.balance = balance;
    }

    //getter
    public int getBalance() {
        return balance;
    }

    //setter
    public void setBalance(int balance) {
        this.balance = balance;
    }

    //提款
    public void withdraw(int amount) { 
        this.balance = getBalance();//先取得Account帳戶還有多少餘額
        交易進行中();//模擬交易處理時間
        balance -= amount;//提款後餘額
        this.setBalance(balance);
    }

    //模擬交易處理時間
    private void 交易進行中() {
        int delay = (int) (Math.random() * 3000);
        try {
            Thread.sleep(delay);//讓執行緒暫停,模擬交易時間
        } catch (InterruptedException ex) {
            System.out.println(ex);
        }
    }

}

//執行緒
class ATM extends Thread {

    private Account account; //宣告

    //建構子
    public ATM(Account account) { //設定帳戶
        this.account = account;
    }

    public void run() {
        String atmName = Thread.currentThread().getName();//取得執行緒名稱

        System.out.println(atmName + "提款中...");
        account.withdraw(1);//對帳戶提款一元
        System.out.println(atmName + "完成提款,餘額:" + account.getBalance());

    }
}

public class ATMTTest {

    public static void main(String[] args) {
        Account acc = new Account(100); //設立帳戶,餘額100
        System.out.println("帳戶餘額:" + acc.getBalance());

        //建立三個執行緒,共同使用一個帳戶
        ATM atm1 = new ATM(acc);
        ATM atm2 = new ATM(acc);
        ATM atm3 = new ATM(acc);
        //設定執行緒名稱
        atm1.setName("ATM1");
        atm2.setName("ATM2");
        atm3.setName("ATM3");
        //啟動        
        atm1.start();
        atm2.start();
        atm3.start();

    }

}

顯示結果


因為沒有進行同步化,所以3台ATM同時提款,這邊要修改為當有ㄧ台ATM在執行,其他兩台ATM要先暫停

同步化方法只允許一個執行緒在其中執行,只要有執行緒在同步化方法 中,其他執行緒必須等候
class Account {

    private int balance;//餘額

    //建構子
    public Account(int balance) {
        this.balance = balance;
    }

    //getter
    public int getBalance() {
        return balance;
    }

    //setter
    public void setBalance(int balance) {
        this.balance = balance;
    }  
    /*
     同步化方法 ( synchronized )
     同步化方法只允許一個執行緒在其中執行,只要有執行緒在同步化方法 中,其他執行緒必須等候
     */
    //提款
    public void withdraw(int amount) { //這邊可以在最前方加入同步化(synchronized)或是在執行緒內容加入
        this.balance = getBalance();//先取得Account帳戶還有多少餘額
        交易進行中();//模擬交易處理時間
        balance -= amount;//提款後餘額
        this.setBalance(balance);
    }

    //模擬交易處理時間
    private void 交易進行中() {
        int delay = (int) (Math.random() * 3000);
        try {
            Thread.sleep(delay);//讓執行緒暫停,模擬交易時間
        } catch (InterruptedException ex) {
            System.out.println(ex);
        }
    }
}

//執行緒
class ATM extends Thread {

    private Account account; //宣告

    //建構子
    public ATM(Account account) { //設定帳戶
        this.account = account;
    }

    public void run() {
        String atmName = Thread.currentThread().getName();//取得執行緒名稱
        //使用同步化synchronized
        //因為多個ATM對同一個帳戶提款需要前一個動作完成才能給下一個提款,所以需要做同步化獨佔
        synchronized(account){ //鎖定account物件(獨佔)
        System.out.println(atmName + "提款中...");
        account.withdraw(1);//對帳戶提款一元
        System.out.println(atmName + "完成提款,餘額:" + account.getBalance());
        }
        //未同步化
//        System.out.println(atmName + "提款中...");
//        account.withdraw(1);//對帳戶提款一元
//        System.out.println(atmName + "完成提款,餘額:" + account.getBalance());

    }
}

public class ATMTTest {

    public static void main(String[] args) {
        Account acc = new Account(100); //設立帳戶,餘額100
        System.out.println("帳戶餘額:" + acc.getBalance());

        //建立三個執行緒,共同使用一個帳戶
        ATM atm1 = new ATM(acc);
        ATM atm2 = new ATM(acc);
        ATM atm3 = new ATM(acc);
        //設定執行緒名稱
        atm1.setName("ATM1");
        atm2.setName("ATM2");
        atm3.setName("ATM3");
        //啟動        
        atm1.start();
        atm2.start();
        atm3.start();

    }
}

顯示結果


這邊有兩種方式,ㄧ種是直接在Account的提款mothod最前方加入synchronized,另一種是採用同步化區塊 ( synchronized blocks ),撰寫程式的彈性較大,執行緒可指定要獨佔哪個物件,並設定同步化程式的範圍

沒有留言:

張貼留言