在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。
//14. 實作類的構造函數,初始化它的屬性值。
public bank(account account) {
//15. 實作任務的 run() 方法。使用 account 的 subtractamount() 方法來讓它的餘額做10次的遞減,遞減額為1000。
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。