并发集合(八)使用原子变量

声明:本文是《 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。

时间: 2024-10-02 04:54:03

并发集合(八)使用原子变量的相关文章

并发集合(九)使用原子 arrays

当你实现一个多个线程共享一个或者多个对象的并发应用时,你就要使用像锁或者同步关键词(例如synchronized)来对他们的属性的访问进行保护,来避免并发造成的数据不一致的错误. 但是这些机制会有以下一些缺点: 死锁(dead lock):例如:当一个线程等待一个锁的时候,会被阻塞,而这个锁被其他线程占用并且永不释放.这种情况就是死锁,程序在这种情况下永远都不会往下执行. 即使只有一个线程在访问共享对象,它也要执行必要的获取锁和释放锁的代码. CAS(compare-and-swap)操作为并发

Java并发编程之原子变量与非阻塞同步机制_java

1.非阻塞算法 非阻塞算法属于并发算法,它们可以安全地派生它们的线程,不通过锁定派生,而是通过低级的原子性的硬件原生形式 -- 例如比较和交换.非阻塞算法的设计与实现极为困难,但是它们能够提供更好的吞吐率,对生存问题(例如死锁和优先级反转)也能提供更好的防御.使用底层的原子化机器指令取代锁,比如比较并交换(CAS,compare-and-swap). 2.悲观技术 独占锁是一种悲观的技术.它假设最坏的情况发生(如果不加锁,其它线程会破坏对象状态),即使没有发生最坏的情况,仍然用锁保护对象状态.

Oracle官方并发教程之原子变量

原文地址,译文地址 ,译者:李任,校对:郑旭东 java.util.concurrent.atomic包定义了对单一变量进行原子操作的类.所有的类都提供了get和set方法,可以使用它们像读写volatile变量一样读写原子类.就是说,同一变量上的一个set操作对于任意后续的get操作存在happens-before关系.原子的compareAndSet方法也有内存一致性特点,就像应用到整型原子变量中的简单原子算法. 为了看看这个包如何使用,让我们返回到最初用于演示线程干扰的Counter类:

java并发编程学习: 原子变量(CAS)

先上一段代码: package test; public class Program { public static int i = 0; private static class Next extends Thread { public void run() { i = i + 1; System.out.println(i); } } public static void main(String[] args) { Thread[] threads = new Thread[10]; for

《Java 7并发编程实战手册》第六章并发集合

由人民邮电出版社出版的<Java 7并发编程实战手册>终于出版了,译者是俞黎敏和申绍勇,该书将于近期上架.之前并发编程网组织翻译过此书,由于邮电出版社在并发网联系他们之前就找到了译者,所以没有采用并发网的译稿,但邮电出版社将于并发网展开合作,发布该书的样章(样章由并发网挑选,你也可以回帖告诉我们你想看哪一章的样章),并组织赠书活动回馈给活跃读者.活动详情请时刻关注并发网的微博和微信(微信号:ifeves),最后祝各位用餐愉快!:) 本章将介绍下列内容: 使用非阻塞式线程安全列表 使用阻塞式线程

并发集合(一)引言

声明:本文是< Java 7 Concurrency Cookbook >的第六章,作者: Javier Fernández González     译者:许巧辉 校对:方腾飞 在本章中,我们将包含: 使用非阻塞线程安全的列表 使用阻塞线程安全的列表 用优先级对使用阻塞线程安全的列表排序 使用线程安全的.带有延迟元素的列表 使用线程安全的NavigableMap 创建并发随机数 使用原子变量 使用原子数组 引言 在编程中,数据结构是一种基本的元素.几乎每个程序都使用一个或多个数据结构类型来存

使用Volatile变量还是原子变量

volatile变量 在Java语言中,volatile变量提供了一种轻量级的同步机制,volatile变量用来确保将变量的更新操作通知到其它线程,volatile变量不会被缓存到寄存器或者对其它处理器不可见的地方,所以在读取volatile变量时总会返回最新写入的值,volatile变量通常用来表示某个状态标识. 原子变量: 原子变量是"更强大的volatile"变量,从实现来看,每个原子变量类的value属性都是一个volatile变量,所以volatile变量的特性原子变量也有.

Clojure的并发(八)future、promise和线程

Clojure 的并发(一) Ref和STM Clojure 的并发(二)Write Skew分析Clojure 的并发(三)Atom.缓存和性能 Clojure 的并发(四)Agent深入分析和Actor Clojure 的并发(五)binding和let Clojure的并发(六)Agent可以改进的地方Clojure的并发(七)pmap.pvalues和pcallsClojure的并发(八)future.promise和线程  八.future.promise和线程 1.Clojure中使

并发集合(六)使用线程安全的NavigableMap

使用线程安全的NavigableMap Java API 提供的有趣的数据结构,并且你可以在并发应用程序中使用,它就是ConcurrentNavigableMap接口的定义.实现ConcurrentNavigableMap接口的类存储以下两部分元素: 唯一标识元素的key 定义元素的剩余数据 每部分在不同的类中实现. Java API 也提供了这个接口的实现类,这个类是ConcurrentSkipListMap,它实现了非阻塞列表且拥有ConcurrentNavigableMap的行为.在内部实