27-运用原子性变量
7.8 运用原子性变量
Java 5的 原子性变量 提供了在单个变量上的原子性操作。在处理普通变量时,每个用Java实现的操作都是在编译程序时转换为JVM可以理解的Java二进制代码指令。例如,当需要给一个变量赋值的时候,仅需使用一条Java指令即可。但是,在编译该程序时,这条指令将会转换为多种JVM语言指令。而当多线程共享一个变量的时候,这将导致数据不一致的问题。
为了避免这些问题,Java提供了原子性变量。在一个线程正在操作一个原子性变量时,如果其他线程想操作同一个变量,那么这会转换其值为本地变量,然后尝试改变旧值为新值。如果旧值与原来一致,则取而代之;否则,该方法开始重新操作。该操作称为 Compare and Set ,即CAS。它采用如下3步来改变变量的值。
1.先获取变量的旧值。
2.在临时变量上改变变量值为新值。
3.如果旧值仍然等于变量的实际值则取而代之。如果其他线程改变了该变量的值,则旧值可以与实际值不同。
有些变量(例如 LongAccumulator
类),接收一个在其内置方法中执行的操作为参数。这些操作必须杜绝任何边际影响,因为它们可能会在每个变量更新操作中执行多次。
原子性变量不使用锁或其他同步机制来保护访问它们的值。而它们的所有操作都是基于Compare and Set。这可以保证几个线程可以同时操作原子性变量而不会产生数据不一致的问题;而且,这简化了实现。
Java 8新增了4个原子性类。首先是 LongAdder
和 DoubleAdder
类,它们存储在不同线程下频繁更新的 long
和 double
的值。当然,也可以通过 AtomicLong
类获得与 LongAdder
相同的功能,但是 LongAdder
具备更好的性能。而另外两个类是 LongAccumulator
和 DoubleAccumulator
。虽然这些类都与前者相似,但是它们可以在构造方法中指定如下两个参数。
- counter的初始化值。
LongBinaryOperator
或者DoubleBinaryOperator
可以指定为一个lambda表达式。该表达式接收一个变量的旧值以及一个自增操作,同时返回变量的新值。
在本节中,我们将介绍如何使用原子性变量来实现一个银行账户和两个不同的任务:一个是给账户添加金额,一个是给账户减少金额。本案例的实现中将会展示 AtomicLong
类的使用技巧。