原子性

原子是世界上的最小单位,具有不可分割性。

比如 a=0;(a非long和double类型) 这个操作是不可分割的,那么我们说这个操作时原子操作。

再比如:a++; 这个操作实际是a = a + 1;是可分割的,所以他不是一个原子操作。非原子操作都会存在线程安全问题,需要我们使用同步技术(sychronized)来让它变成一个原子操作。一个操作是原子操作,那么我们称它具有原子性。

java的concurrent包下提供了一些原子类,我们可以通过阅读API来了解这些原子类的用法。

比如:AtomicInteger、AtomicLong、AtomicReference等。

java.util.concurrent.atomic

一个小型工具包,支持对单个变量进行无锁线程安全编程。本质上,此包中的类将volatile值,字段和数组元素的概念扩展为也提供表单的原子条件更新操作的概念:

boolean compareAndSetexpectedValueupdateValue;

此方法(在不同类的参数类型中有所不同)如果当前包含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();
   }
 }

定义新的实用程序函数很简单,就像getAndIncrement一样,将函数原子地应用于值。

例如,给定一些转变

long transform(long input)

  编写实用程序方法如下:

long getAndTransform(AtomicLong var) {
   long prev, next;
   do {
     prev = var.get();
     next = transform(prev);
   } while (!var.compareAndSet(prev, next));
   return prev; // return next; for transformAndGet
}

访问和更新原子的记忆效应通常遵循挥发性规则,如Java语言规范(17.4内存模型)中所述:

get具有读取volatile变量的记忆效应。

set具有写入(赋值)volatile变量的记忆效应。

lazySet具有写入(赋值)volatile变量的记忆效应,除了它允许对后续(但不是先前的)内存操作进行重新排序,这些内存操作本身不会对普通的非易失性写入施加重新排序约束。在其他用法上下文中,为了垃圾收集,可以在归零时应用lazySet,这是一个永远不会再次访问的引用。

weakCompareAndSet以原子方式读取并有条件地写入变量,但不会创建任何先前发生的排序,因此不提供与weakCompareAndSet目标之外的任何变量的先前或后续读取和写入相关的保证。

compareAndSet和所有其他读取和更新操作(如getAndIncrement)都具有读取和写入volatile变量的内存效果。 除了表示单个值的类之外,此包还包含Updater类,可用于在任何所选类的任何选定volatile字段上获取compareAndSet操作。 AtomicReferenceFieldUpdater,AtomicIntegerFieldUpdater和AtomicLongFieldUpdater是基于反射的实用程序,可提供对相关字段类型的访问。这些主要用于原子数据结构,其中同一节点的几个易失性字段(例如,树节点的链接)独立地受原子更新的影响。这些类在如何以及何时使用原子更新方面提供了更大的灵活性,代价是更加笨拙的基于反射的设置,不太方便的使用和较弱的保证。

AtomicIntegerArray,AtomicLongArray和AtomicReferenceArray类进一步将原子操作支持扩展到这些类型的数组。这些类在为其数组元素提供易失性访问语义方面也值得注意,普通数组不支持这些语义。

原子类还支持weakCompareAndSet方法,其适用性有限。在某些平台上,弱版本可能比普通情况下的compareAndSet更有效,但不同之处在于,任何给定的weakCompareAndSet方法调用都可能虚假地返回false(也就是说,没有明显的原因)。错误返回仅表示如果需要可以重试操作,依赖于当变量保持expectedValue时没有其他线程也尝试设置变量的重复调用的保证最终会成功。 (例如,这种虚假故障可能是由于与预期值和当前值相等无关的内存争用效应。)另外,weakCompareAndSet不提供同步控制通常需要的排序保证。然而,该方法对于更新计数器和统计数据可能是有用的,当这些更新与其他事件无关时 - 在程序的排序之前。当线程看到由weakCompareAndSet引起的原子变量更新时,它不一定会看到对weakCompareAndSet之前发生的任何其他变量的更新。例如,在更新性能统计信息时,这可能是可以接受的,但在其他情况下很少。

AtomicMarkableReference类将单个布尔值与引用相关联。例如,该位可能在数据结构中使用,表示被引用的对象在逻辑上已被删除。 AtomicStampedReference类将整数值与引用相关联。例如,这可以用于表示与一系列更新相对应的版本号。

原子类主要设计为用于实现非阻塞数据结构和相关基础结构类的构建块。 compareAndSet方法不是锁定的一般替代方法。仅当对象的关键更新仅限于单个变量时,它才适用。

原子类不是java.lang.Integer和相关类的通用替换。它们没有定义equals,hashCode和compareTo等方法。 (因为预期原子变量会发生变异,所以它们对于散列表键的选择很差。)此外,仅为那些在预期应用程序中常用的类型提供类。

例如,没有用于表示字节的原子类。在您不希望这样做的那些不常见的情况下,您可以使用AtomicInteger来保存字节值,并适当地进行转换。您还可以使用Float.floatToRawIntBits(float)和Float.intBitsToFloat(int)转换来保存浮点数,并使用Double.doubleToRawLongBits(double)和Double.longBitsToDouble(long)转换加倍。

拓展阅读

参考资料

https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/package-summary.html