1. 程式人生 > >併發集合(八)使用原子變數

併發集合(八)使用原子變數

宣告:本文是《 Java 7 Concurrency Cookbook 》的第一章, 作者: Javier Fernández González 譯者:鄭玉婷 校對:方騰飛

在Java 1.5中就引入了原子變數,它提供對單個變數的原子操作。當你在操作一個普通變數時,你在Java實現的每個操作,在程式編譯時會被轉換成幾個機器能讀懂的指令。例如,當你分配一個值給變數,在Java你只使用了一個指令,但是當你編譯這個程式時,這個指令就被轉換成多個JVM 語言指令。這樣子的話當你在操作多個執行緒且共享一個變數時,就會導致資料不一致的錯誤。

為了避免這樣的問題,Java引入了原子變數。當一個執行緒正在操作一個原子變數時,即使其他執行緒也想要操作這個變數,類的實現中含有一個檢查那步驟操作是否完成的機制。 基本上,操作獲取變數的值,改變本地變數值,然後嘗試以新值代替舊值。如果舊值還是一樣,那麼就改變它。如果不一樣,方法再次開始操作。這個操作稱為 Compare and Set(校對注:簡稱CAS,比較並交換的意思)。

原子變數不使用任何鎖或者其他同步機制來保護它們的值的訪問。他們的全部操作都是基於CAS操作。它保證幾個執行緒可以同時操作一個原子物件也不會出現資料不一致的錯誤,並且它的效能比使用受同步機制保護的正常變數要好。

在這個指南,你將學習怎樣使用原子變數實現一個銀行賬戶和2個不同的任務:一個存錢到賬戶和另一個從賬戶提取錢。在例子的實現中,你將使用 AtomicLong 類。

準備

指南中的例子是使用Eclipse IDE 來實現的。如果你使用Eclipse 或者其他的IDE,例如NetBeans, 開啟並建立一個新的java專案。

怎麼做呢…

按照這些步驟來實現下面的例子:
[ code language=’java’]
//1. 建立一個類,名為 Account,來模擬銀行賬號。
public class Account {

//2. 宣告一個私有 AtomicLong 屬性,名為 balance,用來儲存賬號的餘額。
private AtomicLong balance;

//3. 實現類的建構函式,初始化它的屬性值。
public Account(){
balance=new AtomicLong();
}

//4. 實現一個方法,名為 getBalance(),用來返回餘額屬性值。
public long getBalance() {
return balance.get();
}

//5. 實現一個方法,名為 setBalance(),用來設定餘額屬性值。
public void setBalance(long balance) {
this.balance.set(balance);
}

//6. 實現一個方法,名為 addAmount(),來增加餘額屬性值。
public void addAmount(long amount) {
this.balance.getAndAdd(amount);
}

//7. 實現一個方法,名為 substractAmount() 來減少餘額屬性值。
public void subtractAmount(long amount) {
this.balance.getAndAdd(-amount);
}

//8. 建立一個類,名為 並一定實現 Runnable 介面。這個類會模擬公司付款。
public class Company implements Runnable {

//9. 宣告一個私有 Account 屬性,名為 account。
private Account account;

//10. 實現類的建構函式,初始化它的屬性值。
public Company(Account account) {
this.account=account;
}

//11. 實現任務的 run() 方法。 使用 account 的 addAmount()方法來讓它的餘額做10次的遞增,遞增額為1000。
@Override
public void run() {
for (int i=0; i<10; i++){
account.addAmount(1000);
}
}

//12. 建立一個類,名為 Bank,並一定實現 Runnable 介面。這個類會模擬從一個賬號提款。
public class Bank implements Runnable {

//13. 宣告一個私有 Account 屬性,名為 account。
private Account account;

//14. 實現類的建構函式,初始化它的屬性值。
public Bank(Account account) {
this.account=account;
}

//15. 實現任務的 run() 方法。使用 account 的 subtractAmount() 方法來讓它的餘額做10次的遞減,遞減額為1000。
@Override
public void run() {
for (int i=0; i<10; i++){
account.subtractAmount(1000);
}
}

//16. 建立例子的主類通過建立一個類,名為 Main 並新增 main()方法。
public class Main {

public static void main(String[] args) {

//17. 建立一個 Account 物件,設定它的餘額為 1000。
Account account=new Account();
account.setBalance(1000);

//18. 建立新的 Company 任務和一個執行緒執行它。
Company company=new Company(account);
Thread companyThread=new Thread(company);
// 建立一個新的 Bank t任務和一個執行緒執行它。
Bank bank=new Bank(account);
Thread bankThread=new Thread(bank);

//19. 在操控臺寫上賬號的初始餘額。
System.out.printf(“Account : Initial Balance: %d\n”,account. getBalance());

//20. 開始執行緒。
companyThread.start();
bankThread.start();

//21. 使用 join() 方法等待執行緒的完結並把賬號最終餘額寫入操控臺。
try {
companyThread.join();
bankThread.join();
System.out.printf(“Account : Final Balance: %d\n”,account. getBalance());
} catch (InterruptedException e) {
e.printStackTrace();
}
[/code]

它是怎麼工作的…

這個例子的關鍵是 Account 類。在這個類,我們聲明瞭一個 AtomicLong 屬性,名為 balance,用來儲存賬戶餘額,然後我們使用 AtomicLong 類提供的方法實現了操作餘額的方法。為了實現 getBalance() 方法,返回餘額的屬性值,你要使用 AtomicLong 類的 get() 方法。為了實現 setBalance() 方法,設立餘額值,你要使用 AtomicLong 類的 set() 方法。為了實現 addAmount()方法,為餘額值加上收入,你要使用 AtomicLong 類的getAndAdd() 方法,用特定的引數值增加它並返回值。最後,為了實現 subtractAmount() 方法,減少餘額值,你也要使用 getAndAdd() 方法。

接著,你實現了2個不同的任務:

Company 類模擬了一個公司,增加餘額值。這個類的每次任務會做10次的遞增,遞增值為1000。
Bank 類模擬了一個銀行,銀行作為賬號的擁有者而收取費用。這個類的每次任務會做10次的遞減,遞減值為1000。

在 Main 類,你建立了一個有1000餘額的 Account 物件。然後,你執行一個銀行任務和一個公司任務,所以最終的賬號餘額一定是等同於初始餘額。

當你執行程式,你可以發現你的最終餘額和你的初始值一樣。以下的截圖是例子的執行結果的輸出:

6-8

更多…

記得我們之前提到的,Java還有其他的原子類哦。例如:AtomicBoolean, AtomicInteger, 和 AtomicReference。

參見