这是我参考的一篇文章《基于CAS的乐观锁实现》,讲述的是一种需要CPU支持的执行技术CAS(Compare and Swap)。

首先理解什么是原子性操作,意思是不能再拆分的操作,例如改写一个值,读取一个值都属于原子性操作。

那么CAS是两个操作,先比较旧值,比较通过后再进行改写,这种连合操作合并成一个指令交给CPU,由CPU操作来确保这是一个原子性操作。

多线程同时改写同一个值时,每个线程携带自己的旧值和新值交给CPU改写,CPU的运行是按逐条指令运行,如果发现旧值不符合,线程就会收到改写失败回应。

public final int incrementAndGet() {
for (;;) {
int current = get();
int next = current + 1;
if (compareAndSet(current, next))
return next;
}
}

因此AtomicInteger#incrementAndGet()方法里,会循环尝试使用compareAndSet(...)方法,直到成功为止。

相关的原子类所在包:java.util.concurrent.atomic

  • Atomic + Boolean/Integer/Long/Reference
    操作一个对应的类型对象
  • Atomic + Integer/Long/Reference + Array
    操作一个对应类型的数组

  • Atomic + Integer/Long/Reference + FieldUpdater
    操作一个对应类型的Field对象,类似反射方式改写对象字段

这里《AtomicStampedReference解决ABA问题》,讲述了原子类会出现ABA时带来的隐患,文中举了一个例子,一个单向的链表实现了堆栈操作,使用一个原子变量作为链头指针,现在链头是A,A的next是B。有两个线程分别是T1和T2,他们并发的操作如下:

  • T1:AB => B
  • T2:AB => B => 空 => D => CD => ACD

由于线程T1只认链头是不是A,如果是A,就会将链头指向B,因此可能会出现直接把ACD变成B,这就是ABA并发的隐患。

  • AtomicMarkableReference
    操作一个对象类型和boolean类型的二元组
  • AtomicStampedReference
    操作一个对象类型和int类型的二元组

虽然例子中链头指针是一个原子变量,会出现ABA的情况,但如果再增加一个原子变量,这个原子变量确保不会出现ABA的情况,两个原子变量作为二元组进行原子性操作,即使用AtomicStampedReference就可以有效解决这个ABA的隐患了。

以下是Java8增加的原子类:

  • Striped64
  • Long/Double + Adder
  • Long/Double + Accumulator

这里有两篇文章,内容是分析它们的源码:《从LongAdder 看更高效的无锁实现》《LongAdder和LongAccumulator》

源码有些复杂,我也没看完,Striped64是这项新原子类的基类,它提供的原理是,把一个原子数拆分成多个原子数,最后把这多个原子数合成一个数。换句话说,原本在一个数上做递增或者递减操作的,现在变成在多个数里,选择其中一个做做递增或递减操作,那么加起来的结果与原本方式的结果是等价的。虽然是等价,但它结算结果的过程,是需要把多个数加起来,这个过程已经不是线程安全了,所以它的应用场合相比原本方式会宽一点,原本方式所取出来的值可以作为唯一ID,但现在方式只能用于统计。试想,如果有1000个线程同时在统计同一个数据,那么原本方式的原子类,就会失败率上升,效率也会随之下降。但如果把1000个线程,分成10份,每100个线程统计同一个数据,那么产生10个数据,最后统计的结果就是这10个数据叠加一起的结果,失败率当然因份数的增加而减少,效率也自然有保障。

应用场合:高并发,统计数据。

多线程同步工具——CAS原子变量的更多相关文章

  1. 多线程同步工具——volatile变量

    关于volatile,找了一堆资料看,看完后想找一个方法去做测试,测了很久,感觉跟没有一样. 这本书<深入理解Java内存模型>,对volatile描述中有这样一个比喻的说法,如下代码所示 ...

  2. Java多线程并发编程之原子变量与非阻塞同步机制

    1.非阻塞算法 非阻塞算法属于并发算法,它们可以安全地派生它们的线程,不通过锁定派生,而是通过低级的原子性的硬件原生形式 -- 例如比较和交换.非阻塞算法的设计与实现极为困难,但是它们能够提供更好的吞 ...

  3. Java多线程同步工具类之CountDownLatch

    在过去我们实现多线程同步的代码中,往往使用join().wait().notiyAll()等线程间通信的方式,随着JUC包的不断的完善,java为我们提供了丰富同步工具类,官方也鼓励我们使用工具类来实 ...

  4. Java多线程同步工具类之Semaphore

    Semaphore信号量通常做为控制线程并发个数的工具来使用,它可以用来限制同时并发访问资源的线程个数. 一.Semaphore使用 下面我们通过一个简单的例子来看下Semaphore的具体使用,我们 ...

  5. C++11 多线程同步 互斥锁 条件变量

    在多线程程序中,线程同步(多个线程访问一个资源保证顺序)是一个非常重要的问题,Linux下常见的线程同步的方法有下面几种: 互斥锁 条件变量 信号量 这篇博客只介绍互斥量和条件变量的使用. 互斥锁和条 ...

  6. 多线程同步工具——Lock

    本文原创,转载请注明出处. 参考文章: <"JUC锁"03之 公平锁(一)> <"JUC锁"03之 公平锁(二)> 锁分独占锁与共享锁, ...

  7. Java多线程同步工具类之CyclicBarrier

    一.CyclicBarrier使用 CyclicBarrier从字面上可以直接理解为线程运行的屏障,它可以让一组线程执行到一个共同的屏障点时被阻塞,直到最后一个线程执行到指定位置,你设置的执行线程就会 ...

  8. 多线程同步工具——LockSupport

    用例1:子线程等待主线程发放许可! public static void main(String[] args) { Thread thread = new Thread(){ public void ...

  9. Java多线程-----原子变量和CAS算法

       原子变量      原子变量保证了该变量的所有操作都是原子的,不会因为多线程的同时访问而导致脏数据的读取问题      Java给我们提供了以下几种原子类型: AtomicInteger和Ato ...

随机推荐

  1. 【.net 深呼吸】细说CodeDom(5):类型成员

    前文中,老周已经厚着脸皮介绍了类型的声明,类型里面包含的自然就是类型成员了,故,顺着这个思路,今天咱们就了解一下如何向类型添加成员. 咱们都知道,常见的类型成员,比如字段.属性.方法.事件.表示代码成 ...

  2. 重撸JS_1

    1.声明 用 var 或 let 声明的未赋初值的变量,值会被设定为undefined(译注:即未定义值,本身也是一个值) 试图访问一个未初始化的变量会导致一个 ReferenceError 异常被抛 ...

  3. Lambda

    Lambda Lambda 表达式是一种可用于创建委托或表达式目录树类型的匿名函数. 通过使用 lambda 表达式,可作为参数传递或作为函数调用值返回的本地函数. Lambda 表达式对于编写 LI ...

  4. jquery插件的用法之cookie 插件

    一.使用cookie 插件 插件官方网站下载地址:http://plugins.jquery.com/cookie/ cookie 插件的用法比较简单,直接粘贴下面代码示例: //生成一个cookie ...

  5. NPM如何更新到最新版

    参考文章--npm更新到最新版本的方法 其实我们可以这样,随便新建一个文件夹例如:F:\test.按着"shift"键,右键该文件夹,选择"在此处打开命令窗口(W)&qu ...

  6. Android之数据存储的五种方法

    1.Android数据存储的五种方法 (1)SharedPreferences数据存储 详情介绍:http://www.cnblogs.com/zhangmiao14/p/6201900.html 优 ...

  7. Mono on CentOS 6.3 安装笔记

    在网上找到篇关于yum的文章,写的很详细特此推荐下,在阅读本文之前建议先看看它: <CentOS yum源的配置与使用> 如果用的是移动光驱,建议不要开启本地的yum 的本地reposit ...

  8. centos6.3与jexus5.4.4配置支持php(wordpress)

    centos6.3与jexus5.4.4配置支持php,并搭建自己的wordpress博客,供那些在Linux平台下想让 php和asp.net一起跑的初学者参考. 1.搭建webserver 首先准 ...

  9. Xamarin.Android之Fragment Walkthrough

    利用Fragment设计能够兼容不同屏幕的应用 这里我们先围观下最后的成果图,给读者打打气: 普通手机上显示的结果: 在平板上显示的结果: 笔者要郑重声明下,虽然看似是两种不同的显示效果,但是同一个应 ...

  10. 【转】 C#中Finally的一个不太常见的用法

    原文地址:http://www.cnblogs.com/listhome/p/3664300.html 最近在看.net BCL 传送门 的源码. 在 System.Collections.Concu ...