与基于锁的方案相比,非阻塞算法在设计和实现上都要负责得多,但它们在可伸缩性和活跃性上拥有巨大的优点。 原子变量提供了与volatile类型变量相同的内存语义,此外还支持原子的更新操做,从而使它们更加适用于实现计数器、序列发生器和统计数据收集等,同时还能比基于锁的方法提供更高的可伸缩性。 独占锁是一种悲观技术----它假设最坏的状况。 如今,几乎全部的现代处理器中都包含了某种形式的原子读-改-写指令,例如比较并交换(Compare-and-Swap)或者关联加载/条件存储(Load-Linked/Store-Condition)。算法
CAS包含了3个操做数----须要读写的值V、进行比较的值A和拟写入的新值B。当且仅当V的值等于A时,CAS才会经过原子方式用新值B来更新V的值,不然不会执行任何操做,不管V的值是否等于A,都将返回V的原值。 当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量的值,而其余线程都将失败,然而,失败的线程并不会挂起,而是被告知在此次竞争中失败,并能够再次尝试。数组
在Java5.0中引入了地城的支持,在int、long和对象的引用等类型上都公开了CAS操做,而且JVM把它们编译为底层提供的最有效方法。线程
共有12个原子变量类,可分为4组:标量类(Scalar)、更新器类、数组类以及复合变量类。最经常使用的原子变量就是标量类:AtomicInteger、AtomicLong、AtomicBoolean以及AtomicReference。全部这些类都支持CAS,此外,AtomicInteger和AtominLong还支持算术运算。设计
若是在某种算法中,一个线程的失败或挂起不会致使其余线程也失败或挂起,那么这种算法就被称为非阻塞算法。若是在算法的每一个步骤中都存在某个线程可以执行下去,那么这种算法也被称为无锁(Lock-Free)算法。对象
ABA问题是一种异常现象:若是在算法中的节点能够被循环使用,那么在使用“比较并交换”指令时就可能出现这种问题。在CAS操做中将判断“V的值是否仍然为A?”,而且若是是的话就继续执行更新操做,在大多数状况下,包括本章给出的示例,这种判断是彻底足够的。然而,有时候还须要知道“自从上次看到V的值为A以来,这个值是否发生了变化?”,在某些算法中,若是V的值首先由A变成B,再由B变成A,那么仍然被认为是发生了变化,并须要从新执行算法中的某些步骤。 解决ABA问题的一个简单方法是:不是更新某个引用的值,而是更新两个值,包括一个引用和一个版本号。即便这个值由A变为B,而后又变为A,版本号也将是不一样的。 AtomicStampedReference、AtomicMarkableReference支持在两个变量上执行原子的条件更新。AtomicStampedReference将更新一个“对象-引用”二元组,经过在引用上加上“版本号”,从而避免ABA问题。AtomicMarkableReference将更新一个“对象引用-布尔值”二元组,在某些算法中将经过这种二元组使节点保存在链表中同时又将其标记为“已删除的节点”。内存