本文介紹java最基本的同步方式,即使用synchronized關鍵字來控制一個方法的並發訪問,如果一個對象已用synchronized關鍵字聲明,那么只有一個執行線程允許去訪問它,其它試圖訪問這個對象的線程將被掛起,直到第一個線程訪問完畢。
下面通過一個小例子來學習這個概念,公司向銀行存錢,取錢場景。
1:創建Account的賬號類,它是銀行賬戶的模型,只有一個雙精度浮點型屬性,balance.
2:實現balance的get set 方法。
3:實現AddAmount()方法,將傳入的數量加到余額balance中,並且在同一時間只允許一個線程去改變這個值,使用synchronized關鍵字。
4:實現SubtractAmount()方法,將傳入的數量從余額balance中扣除,並且在同一時間只允許一個線程去改變這個值。
具體代碼:
1 public class Account { 2 3 /** 4 * Balance of the bank account 5 */ 6 private double balance; 7 8 /** 9 * Returns the balance of the account 10 * @return the balance of the account 11 */ 12 public double getBalance() { 13 return balance; 14 } 15 16 /** 17 * Establish the balance of the account 18 * @param balance the new balance of the account 19 */ 20 public void setBalance(double balance) { 21 this.balance = balance; 22 } 23 24 /** 25 * Add an import to the balance of the account 26 * @param amount import to add to the balance 27 */ 28 public synchronized void addAmount(double amount) { 29 double tmp=balance; 30 try { 31 Thread.sleep(10); 32 } catch (InterruptedException e) { 33 e.printStackTrace(); 34 } 35 tmp+=amount;
balance=tmp; 36 } 37 38 /** 39 * Subtract an import to the balance of the account 40 * @param amount import to subtract to the balance 41 */ 42 public synchronized void subtractAmount(double amount) { 43 double tmp=balance; 44 try { 45 Thread.sleep(10); 46 } catch (InterruptedException e) { 47 e.printStackTrace(); 48 } 49 tmp-=amount; 50 balance=tmp; 51 } 52 53 }
5:實現一個ATM模擬類Bank,它使用subtractAmount()方法對賬戶的余額進行扣除,實現Runabl接口。
具體代碼:
1 public class Bank implements Runnable { 2 3 /** 4 * The account affected by the operations 5 */ 6 private Account account; 7 8 /** 9 * Constructor of the class. Initializes the account 10 * @param account The account affected by the operations 11 */ 12 public Bank(Account account) { 13 this.account=account; 14 } 15 16 17 /** 18 * Core method of the Runnable 19 */ 20 public void run() { 21 for (int i=0; i<100; i++){ 22 account.subtractAmount(1000); 23 } 24 } 25 26 }
6:實現公司模擬類,調用addAmount()方法進行存錢,實現Runabl接口。
具體代碼:
1 public class Company implements Runnable { 2 3 /** 4 * The account affected by the operations 5 */ 6 private Account account; 7 8 /** 9 * Constructor of the class. Initializes the account 10 * @param account the account affected by the operations 11 */ 12 public Company(Account account) { 13 this.account=account; 14 } 15 16 /** 17 * Core method of the Runnable 18 */ 19 public void run() { 20 for (int i=0; i<100; i++){ 21 account.addAmount(1000); 22 } 23 }
7:在主方法中調用測試:通過線程的join方法,在存期那,取錢線程模擬完畢后打印出結構。
1 public class Main { 2 3 /** 4 * Main method of the example 5 * @param args 6 */ 7 public static void main(String[] args) { 8 // Creates a new account ... 9 Account account=new Account(); 10 // an initialize its balance to 1000 11 account.setBalance(1000); 12 13 // Creates a new Company and a Thread to run its task 14 Company company=new Company(account); 15 Thread companyThread=new Thread(company); 16 // Creates a new Bank and a Thread to run its task 17 Bank bank=new Bank(account); 18 Thread bankThread=new Thread(bank); 19 20 // Prints the initial balance 21 System.out.printf("Account : Initial Balance: %f\n",account.getBalance()); 22 23 // Starts the Threads 24 companyThread.start(); 25 bankThread.start(); 26 27 try { 28 // Wait for the finalization of the Threads 29 companyThread.join(); 30 bankThread.join(); 31 // Print the final balance 32 System.out.printf("Account : Final Balance: %f\n",account.getBalance()); 33 } catch (InterruptedException e) { 34 e.printStackTrace(); 35 } 36 } 37 }
結果,相同時間內,存與取執行后應該是相等的。如果我們在方法中不去使用synchronized關鍵字,那么得出的結果就不對了。
Account : Initial Balance: 1000.000000
Account : Final Balance: 1000.000000