# 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(); } } ```