1. 程式人生 > >Java多執行緒同步---以銀行存取錢的過程的簡單程式碼例項

Java多執行緒同步---以銀行存取錢的過程的簡單程式碼例項

首先存錢取錢的這個操作,應該是執行緒操作的,可以有很多的顧客,這意思就是得有多個執行緒,多個執行緒之間共同操作一個銀行,銀行的金額就需要同步。才能保證執行緒安全。

所以,下面就把這個程式碼的例項放 這,有不對的地方,還請指出來哈。因為有個老鐵問這個多執行緒的程式碼。

首先是銀行,這個物件model的建立。

package com.lxk.threadTest.bank;

/**
 * 銀行model,一個總金額屬性。
 * <p>
 *
 * @author lxk on 2017/6/26
 */
public class Bank {
    /**
     * 給銀行個啟動資金,不然怎麼幹生意呢。
     */
    private int sum = 200;
    //這個從來不這麼用,但也算是正確的一種加鎖的機制:同步程式碼塊。
    //Object obj = new Object();

    /**
     * 存錢
     * 要是不加[synchronized--同步函式],則會出現多執行緒安全問題。
     */
    public synchronized void add(int n) {
        //synchronized (obj) {
        sum = sum + n;
        try {
            Thread.sleep(10);
        } catch (Exception ignore) {
        }
        //當存錢次數變多的時候,就可以發現,存錢的執行緒確實是2個在交替執行存錢這個動作的。
        System.out.println(Thread.currentThread().getName() + "...sum=" + sum);
        //}
    }

    /**
     * 取錢
     * 要是不加[synchronized--同步函式],則會出現多執行緒安全問題。
     */
    public synchronized void reduce(int n) {
        if (sum - n >= 0) {
            sum = sum - n;
        } else {
            System.out.println("bank's money is not enough !");
        }
        try {
            Thread.sleep(30);
        } catch (Exception ignore) {
        }
        //當存錢次數變多的時候,就可以發現,存錢的執行緒確實是2個在交替執行存錢這個動作的。
        System.out.println(Thread.currentThread().getName() + "...sum=" + sum);
    }
}

在程式碼裡面有存和取2個 方法,這2個方法,以及一個總金額,裡面有部分被註釋掉的程式碼,那個是簡單易懂好理解的,多執行緒加鎖互斥,保證執行緒間同步的方法。

但是這個是不常用的方法,常用的就是使用synchronized這個關鍵字來修飾同步方法。

客戶物件的model

package com.lxk.threadTest.bank;

/**
 * 顧客,實現runnable()介面,多個人可以一起存錢
 *
 * @author lxk on 2017/6/26
 */
public class Customer implements Runnable {

    /**
     * 存錢型別
     */
    static final String TYPE_ADD = "add";
    /**
     * 取錢型別
     */
    static final String TYPE_REDUCE = "reduce";
    /**
     * 銀行
     */
    private Bank bank;
    /**
     * 對錢的操作型別,存錢or取錢
     */
    private String type;
    /**
     * 操作的次數,理論上是個正數
     */
    private int time;
    /**
     * 要存或者取多少錢
     */
    private int money;

    public Customer() {
    }

    public Customer(Bank bank, String type, int time, int money) {
        this.bank = bank;
        this.type = type;
        this.time = time;
        this.money = money;
    }

    @Override
    public void run() {
        for (int x = 0; x < time; x++) {
            if (TYPE_ADD.equals(type)) {
                bank.add(money);
            } else if (TYPE_REDUCE.equals(type)) {
                bank.reduce(money);
            }
        }
    }
}

客戶物件,因為可以很多個客戶同時訪問一個銀行,所以,這個存錢取錢的操作就用執行緒來實現。

屬性就構造方法傳值了。

main方法

package com.lxk.threadTest.bank;

/**
 * 銀行存錢的多執行緒例項
 * <p>
 * 【需求:】
 * 銀行有一個金庫。
 * 有兩個儲戶分別存或者取n * 100。
 * 目的:該程式是否有安全問題,如果有,如何解決?
 * <p>
 * 【如何找問題:】
 * 1,明確哪些程式碼是多執行緒執行程式碼。
 * 2,明確共享資料。
 * 3,明確多執行緒執行程式碼中哪些語句是操作共享資料的。
 *
 * @author lxk on 2017/6/26
 */
public class Main {
    public static void main(String[] args) {
        //一個銀行and多個客戶
        Bank bank = new Bank();
        int time = 10000;
        int money = 100;
        //這個客戶存錢
        Customer c1 = new Customer(bank, Customer.TYPE_ADD, time, money);
        //這個客戶取錢
        Customer c2 = new Customer(bank, Customer.TYPE_REDUCE, time, money);

        Thread t1 = new Thread(c1);
        Thread t2 = new Thread(c2);
        t1.start();
        t2.start();
    }
}

上述程式碼實際執行效果如下圖。

這個存取錢的次數要是小了,就可能會看到2個執行緒有先後順序,所以,這個次數咱整多點,然後,就看到如圖所示的情況,執行緒1是取錢的,執行緒0時存錢的,可以看到2個執行緒是互相交錯執行的,有存有取,沒有規律可言。

這個就保證了資料的同步了。

至於如何才能不同步,也就是異常的現象,

你可以把add方法的這個synchronized 關鍵字去掉之後,把次數調小一點改成3次,sum的初始值給設定成0.你再試試程式碼,

就會發現所謂的不同步現象。


上圖的右邊就是不同步的結果,2個人每次存100,存三次,總數是不是得,100,200,300,400,500,600.得長。

但是,執行結果卻不是的,

這個時候,你再把synchronized給add方法加上去,就會出現左邊的圖的結果,這個就是正確的結果。

我是為了,有存有取,所以,就又加了個方法。程式碼就變成上面的樣子啦。

差不多都是執行緒間同步的例子啦。

我就簡單記錄下程式碼。用的時候,可以分分鐘就拿出來。