concurrent.atomic包简介
类的小工具包,支持在单个变量上解除锁的线程安全编程。事实上,此包中的类可将volatile值、字段和数组元素的概念扩展到那些也提供原子条件更新操作的类,其形式如下:
boolean compareAndSet(expectedValue, updateValue);
如果此方法(在不同的类间参数类型也不同)当前保持expectedValue,则以原子方式将变量设置为updateValue,并在成功时报告true。此包中的类还包含获取并无条件设置值的方法,以及以下描述的较弱条件的原子更新操作weakCompareAndSet。
这些方法的规范使实现能够使用当代处理器上提供的高效机器级别原子指令。但是在某些平台上,该支持可能需要某种形式的内部锁。因而,该方法不能严格保证不被阻塞 - 执行操作之前可能暂时阻塞线程。
类 AtomicBoolean、AtomicInteger、AtomicLong 和 AtomicReference的实例各自提供对相应类型单个变量的访问和更新。每个类也为该类型提供适当的实用工具方法。例如,类AtomicLong和AtomicInteger提供了原子增量方法。一个应用程序将按以下方式生成序列号:
class Sequencer {
private final AtomicLong sequenceNumber = new AtomicLong(0);
public long next() {
return sequenceNumber.getAndIncrement();
}
}
原子访问和更新的内存效果一般遵循以下可变规则,正如 The Java Language Specification, Third Edition (17.4 Memory Model) 中的声明:
get具有读取volatile变量的内存效果。
set具有写入(分配)volatile 变量的内存效果。
除了允许使用后续(但不是以前的)内存操作,其自身不施加带有普通的非volatile写入的重新排序约束,lazySet具有写入(分配)volatile 变量的内存效果。在其他使用上下文中,当为null时(为了垃圾回收),lazySet可以应用不会再次访问的引用。
weakCompareAndSet以原子方式读取和有条件地写入变量但不创建任何 happen-before排序,因此不提供与除weakCompareAndSet目标外任何变量以前或后续读取或写入操作有关的任何保证。
compareAndSet和所有其他的读取和更新操作(如getAndIncrement)都有读取和写入volatile变量的内存效果。
除了包含表示单个值的类之外,此包还包含 Updater类,该类可用于获取任意选定类的任意选定volatile字段上的compareAndSet操作。AtomicReferenceFieldUpdater、AtomicIntegerFieldUpdater和AtomicLongFieldUpdater是基于反射的实用工具,可以提供对关联字段类型的访问。它们主要用于原子数据结构中,该结构中同一节点(例如,树节点的链接)的几个 volatile 字段都独立受原子更新控制。这些类在如何以及何时使用原子更新方面具有更大的灵活性,但相应的弊端是基于映射的设置较为拙笨、使用不太方便,而且在保证方面也较差。
AtomicIntegerArray、AtomicLongArray和AtomicReferenceArray类进一步扩展了原子操作,对这些类型的数组提供了支持。这些类在为其数组元素提供volatile访问语义方面也引人注目,这对于普通数组来说是不受支持的。
原子类也支持weakCompareAndSet方法,该方法具有受限制的适用性。在某些平台上,弱版本在正常情况下可能比 compareAndSet 更有效,但不同的是 weakCompareAndSet 方法的任何给定调用可能意外返回 false(即没有明确的原因)。返回 false 仅意味着可以在需要时重新尝试操作,具体取决于重复执行调用的保证,当该变量保持 expectedValue 并且没有其他线程也在尝试设置该变量时,最终将获得成功。(例如,这样的虚假失败可能是由于内存争用的结果,该争用与期望值和当前值是否相等无关)。
此外,weakCompareAndSet 不提供通常需要同步控制的排序保证。但是,在这样的更新与程序的其他 happen-before 排序不相关时,该方法可用于更新计数器和统计数据。当一个线程看到对weakCompareAndSet导致的原子变量的更新时,它不一定能看到在 weakCompareAndSet之前发生的对任何其他变量的更新。例如,在更新性能统计数据时,这也许可以接受,但其他情况几乎不可以。
AtomicMarkableReference类将单个布尔值与引用关联起来。例如,可以在数据结构内部使用此位,这意味着引用的对象在逻辑上已被删除。AtomicStampedReference类将整数值与引用关联起来。例如,这可用于表示与更新系列对应的版本号。
设计原子类主要用作各种构造块,用于实现非阻塞数据结构和相关的基础结构类。compareAndSet方法不是锁的常规替换方法。仅当对象的重要更新限定于单个变量时才应用它。
原子类不是java.lang.Integer和相关类的通用替换方法。它们不定义诸如 hashCode和compareTo之类的方法。(因为原子变量是可变的,所以对于哈希表键来说,它们不是好的选择。)另外,仅为那些通常在预期应用程序中使用的类型提供类。例如,没有表示 byte 的原子类。这种情况不常见,如果要这样做,可以使用 AtomicInteger 来保持 byte 值,并进行适当的强制转换。也可以使用 Float.floatToIntBits 和 Float.intBitstoFloat 转换来保持 float
值,使用 Double.doubleToLongBits 和 Double.longBitsToDouble 转换来保持 double 值。
对AtomicInteger源码的理解
下两点:
private volatile int value;
public final int get() {
return value;
}
public final void set(int newValue) {
value = newValue;
}
1.volatile修饰变量,保证get()/set()的原子性
2.利用系统底层的CAS原语来实现非阻塞的其它方法原子操作
private volatile int value;
- 如果当前值
==
预期值,则以原子方式将当前值 设置为给定的更新值。 -
- 参数:
expect
- 预期值update
- 新值- 返回:
- 如果成功,则返回 true。返回 False 指示实际值与预期值不相等。
public final int getAndIncrement() {
for (;;) {
1 int current = get();
2 int next = current + 1;
3 if (compareAndSet(current, next))
return current;
}
}
单看这段 代码 别说 保证原子性 即使+1 ,他都很难保证, 因为根本没有更新value的操作
重点在于compareAndSet() 函数
compareAndSet
public final boolean compareAndSet(int expect,
int update)
该函数 只有两个参数,可操作的确实三个值 ,即 value ,expect, update. 他使用了 由硬件保证其原子性的指令 CAS (compare and swap)。
compareAndSet 函数保证了 比较,赋值这两步操作可以通过一个原子操作完成。
然后看整个函数, 所有代码被放到了一个循环里面, 如果compareAndSet()执行失败,则说明 在int current = get(); 以后,其他线程对value进行了更新, 于是就循环一次,重新获取当前值,直到compareAndSet()执行成功为止。
综上,getAndIncrement() 方法并不是原子操作。 只是保证了他和其他函数对 value 值得更新都是有效的。
他所利用的是基于冲突检测的乐观并发策略。 可以想象,这种乐观在线程数目非常多的情况下,失败的概率会指数型增加。
参考
JDK 1.6 AtomicInteger
《Java并发编程实践》