Java併發編程初級篇(十):synchronized同步方法

在Java中咱們可使用synchronized關鍵字來控制一段代碼的併發訪問。使用synchronized關鍵字修飾的代碼在同一時間只有一個線程能夠訪問,其餘要訪問這個代碼塊的線程將被掛起。java

使用synchronized關鍵字能夠控制併發訪問同時修改一份數據形成數據不一致的問題,可是synchronized會下降系統性能,由於即便你啓動再多的線程,同一時間仍是隻有一個線程能訪問,其餘線程都在掛起,因此使用了synchronized也就沒有真正意義上的併發了。併發

咱們可使用synchronized關鍵字修飾方法,那麼這個方法就是同步方法。咱們也可使用synchronized來修飾代碼款,那麼這個代碼塊就是同步代碼塊。ide

示例代碼

下面咱們看一個經典的銀行帳號的例子,在這個例子中咱們模擬一個銀行帳號的存取款過程,同時開啓多個線程模擬屢次併發的存取款操做,最後咱們來看下帳戶餘額。性能

建立Account類來模擬銀行帳戶,其中修改帳戶金額的方法使用synchronized關鍵字修飾,打印帳戶變更並模擬每次帳戶修改操做須要1秒鐘完成。this

public class Account {
    private int amount;

    public Account(int amount) {
        this.amount = amount;
    }

    public int getAmount() {
        return amount;
    }

    public void setAmount(int amount) {
        this.amount = amount;
    }

    //public int modify(int amount) {
    public synchronized int modify(int amount) {
        if (amount > 0) {
            System.out.printf("%s: 向帳戶存款%d元.\n", Thread.currentThread().getName(), amount);
        } else if (amount < 0 && this.amount >= -amount) {
            System.out.printf("%s: 餘額充足,從帳戶取款%d元.\n", Thread.currentThread().getName(), amount);
        } else if (amount < 0 && this.amount < -amount) {
            System.out.printf("%s: 餘額不足,取款失敗.\n", Thread.currentThread().getName());
            return this.amount;
        } else {
            return this.amount;
        }

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        this.amount = this.amount + amount;

        return amount;
    }
}

新建線程類模擬用戶的存取款操做spa

public class MyRunnable implements Runnable{
    private Account account;
    private int amount;

    public MyRunnable(Account account, int amount) {
        this.account = account;
        this.amount = amount;
    }

    @Override
    public void run() {
        account.modify(amount);
    }
}

最後再主方法類中,新建一個餘額爲1000元的帳戶,而後使用4個線程分別模擬1次存款操做,3次取款操做,正常狀況應該是有一次取款失敗,由於最後帳戶餘額已經不夠1000元了。線程

public class Main {
    public static void main(String[] args) {
        Account account = new Account(1000);

        Thread[] threads = new Thread[4];
        threads[0] = new Thread(new MyRunnable(account, 1000));
        threads[1] = new Thread(new MyRunnable(account, -1000));
        threads[2] = new Thread(new MyRunnable(account, -1000));
        threads[3] = new Thread(new MyRunnable(account, -1000));

        try {
            for (int i = 0; i < 4; i++) {
                threads[i].start();
                Thread.sleep(50);
            }

            for (int i = 0; i < 4; i++) {
                 threads[i].join();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.printf("Main: 帳戶餘額:%d\n", account.getAmount());
    }
}

查看日誌,咱們發現有一次取款失敗,而且最後帳戶餘額爲0.日誌

Thread-0: 向帳戶存款1000元.
Thread-3: 餘額充足,從帳戶取款-1000元.
Thread-2: 餘額充足,從帳戶取款-1000元.
Thread-1: 餘額不足,取款失敗.
Main: 帳戶餘額:0

 咱們若是去掉synchronized關鍵字修飾,你會發現三次取款都成功了,而且最後帳戶餘額爲-1000元。code

Thread-0: 向帳戶存款1000元.
Thread-1: 餘額充足,從帳戶取款-1000元.
Thread-2: 餘額充足,從帳戶取款-1000元.
Thread-3: 餘額充足,從帳戶取款-1000元.
Main: 帳戶餘額:-1000
相關文章
相關標籤/搜索