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