原子变量在 Java 5 中被引入以在单个变量上提供原子操作。 对于普通变量,在编译程序时,用Java实现的每个操作被转化为数条机器能理解的指令。 例如赋值给一个变量,在Java中只用一条语句,但编译此程序时,这条语句被转化为JVM语言的多条指令。 此事实在多个线程共享一个变量时能造成数据不一致的错误。
为了避免此问题,Java 引入了原子变量。当一个线程操作一个原子变量时,如果其他线程想操作同一个变量, 原子变量类的实现包含一个检查操作是否一步完成的机制。基本上是:
此操作被称为 比较并设置。
原子变量不使用锁或其他同步机制来保护对其值的访问。所有操作都基于比较并设置操作。 能保证几个线程同时操作一个原子变量也不会产生数据不一致的问题,并且性能比用同步机制保护的普通变量要好。
本节的示例代码在 com.elanzone.books.noteeg.chpt6.sect08 package中
数据类 : Account
private AtomicLong balance; public Account() { balance = new AtomicLong(); }
public long getBalance() { return balance.get(); }
public void setBalance(long amount) { balance.set(amount); }
public void addAmount(long amount) { balance.getAndAdd(amount); }
public void subtractAmount(long amount) { this.balance.getAndAdd(-amount); }
Runnable 实现类 : Company
private Account account; public Company(Account account) { this.account = account; }
@Override public void run() { for (int i = 0; i < 10; i++) { account.addAmount(1000); } }
Runnable 实现类 : Bank
private Account account; public Bank(Account account) { this.account = account; }
@Override public void run() { for (int i = 0; i < 10; i++) { account.subtractAmount(1000); } }
控制类 : Main
Account account = new Account(); account.setBalance(1000); Company company = new Company(account); Thread companyThread = new Thread(company); Bank bank = new Bank(account); Thread bankThread = new Thread(bank); System.out.printf("Account : Initial Balance: %d\n", account.getBalance()); companyThread.start(); bankThread.start(); try { companyThread.join(); bankThread.join(); System.out.printf("Account : Final Balance: %d\n", account.getBalance()); } catch (InterruptedException e) { e.printStackTrace(); }
本例的关键在于 Account 类。在此类中,声明了一个名为 balance 的 AtomicLong 变量以保存账户余额。 然后实现了使用 AtomicLong 类提供的方法来操作余额的方法。
接着,实现了2个不同的任务:
在 Main 类中,创建一个余额有1000的Account对象。然后执行一个 bank 任务和一个 company 任务。 所以账户的最终余额必须和初始余额一致。