並發集合(八)使用原子變量
聲明:本文是《 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 對象。然後,你運行一個銀行任務和一個公司任務,所以最終的賬號餘額一定是等同於初始餘額。
當你運行程序,你可以發現你的最終餘額和你的初始值一樣。以下的截圖是例子的運行結果的輸出:
更多…
記得我們之前提到的,Java還有其他的原子類哦。例如:AtomicBoolean, AtomicInteger, 和 AtomicReference。
參見
文章轉自 並發編程網-ifeve.com
最後更新:2017-05-23 17:03:35