---
# System prepended metadata

title: Java - Synchronization & Lock
tags: [Java, Basic Java]

---

# Java - Synchronization & Lock
###### tags: `Java` `Basic Java`

目的：控管執行緒的執行，確保執行緒安全

舉個實際的例子：銀行帳戶取款

若兩人共用一個銀行戶頭，兩人分別去帳戶取錢就是一個多執行緒
但如果兩人同時要把銀行帳戶餘額取光的時候，在沒有控管的情況下就會造成餘額顯示異常

因此要把領錢這個動作用sychronized()裝起來，也就是上鎖。
先拿到鎖的thread就會先進行動作

## 1. synchronized(): code
重點：synchronized的規範
1. 若使用instant進行上鎖，建議使用this(i.e. synchronized(this))
2. 若使用靜態方法，則用類別.class上鎖

其實也可以用任一唯一物件上鎖(例如用String)，但非常不切實際，因為這樣上鎖之後就是全部的thread都共用同一個鎖

完整示範source code

銀行帳戶類別
```
public class Account {
    private String cardID;
    private double account;

    public Account() {

    }

    public Account(String cardID, double account) {
        this.cardID = cardID;
        this.account = account;
    }

    public String getCardID() {
        return cardID;
    }

    public void setCardID(String cardID) {
        this.cardID = cardID;
    }

    public double getAccount() {
        return account;
    }

    public void setAccount(double account) {
        this.account = account;
    }

    public void withdraw(double money) {
        // get name of thread(which is name of withdrawer)
        String user = Thread.currentThread().getName();

        // the following code should be locked
        synchronized (this) {
            if (this.account >= money) {
                // message
                System.out.println(user + " successfully withdrew " + money);
                // update account
                this.account -= money;
                System.out.println("Remaining: " + this.account);
            } else {
                System.out.println(user + " :withdrew failure: not sufficient fund");
            }
        }
    }
}
```

領錢的動作(Thread)
```
public class Withdraw extends Thread {

    private Account newAccount;

    public Withdraw(Account newAccount, String name) {
        super(name);
        this.newAccount = newAccount;
    }

    public Withdraw(Account newAccount) {
        this.newAccount = newAccount;
    }

    @Override
    public void run() {
        newAccount.withdraw(100000);
    }
}
```

Main Method
```
public class ThreadDemo {

    public static void main(String[] args) {

        // create a new account
        Account newAccount = new Account("Mutual Account01", 100000);

        // create a new withdrawing thread
        new Withdraw(newAccount, "John").start();
        new Withdraw(newAccount, "Marry").start();
    }
}
```

## 2. synchronized(): method
同上例，只需要改出問題的方法即可
```
    public synchronized void withdraw(double money) {
        // get name of thread(which is name of withdrawer)
        String user = Thread.currentThread().getName();

        // the following code should be lock
            if (this.account >= money) {
                // message
                System.out.println(user + " successfully withdrew " + money);
                // update account
                this.account -= money;
                System.out.println("Remaining: " + this.account);
            } else {
                System.out.println(user + " :withdrew failure: not sufficient fund");
            }
    }
```


## 3. Lock API

使用實例類別ReentrantLock，詳細可參考Java API 文件
注意點：
1. 使用try catch撰寫，並需要在finally解鎖
2. 用final修飾，確保鎖不會被更改或替換

舉例
```
public class Account {
    private String cardID;
    private double account;
    
    // create lock object
    private final Lock lock = new ReentrantLock();
    (...)
```
```
    public void withdraw(double money) {
        // get name of thread(which is name of withdrawer)
        String user = Thread.currentThread().getName();
        
        lock.lock();
        try {            
            // the following code should be lock
            if (this.account >= money) {
                // message
                System.out.println(user + " successfully withdrew " + money);
                // update account
                this.account -= money;
                System.out.println("Remaining: " + this.account);
            } else {
                System.out.println(user + " :withdrew failure: not sufficient fund");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
```