龙空技术网

Java并发包,你需要掌握的atomic

程序员面试工具箱 477

前言:

今天兄弟们对“高并发cas”大体比较关注,你们都需要了解一些“高并发cas”的相关资讯。那么小编在网摘上收集了一些有关“高并发cas””的相关知识,希望看官们能喜欢,看官们快快来学习一下吧!

前面我用大量的篇幅分析了JDK中关于原子操作的解决方案,在不挂起线程的情况下,能够实现单一原子的操作,利用关键字volatile+CAS操作实现了Java的乐观锁。atomic包中的类的思维导向图如下:

上面把atomic包中的类分为了5大类,分别如下:

第一类:关于单一变量的原子操作,我说的单一变量是从广义上讲的,例如一个变量或者一个对象,主要包含如下类:AtomicBoolean、AtomicInteger、AtomicLong、AtomicReference第二类:关于数组中元素的原子操作,这里的原子操作仅仅对数组中的元素,与数组本身没有关系,主要包含如下类:AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray第三类:关于对象中字段的,这一类有很多的限制,一般不太常用,主要包含如下类:AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicReferenceFieldUpdater第四类:关于解决ABA问题的,主要包含如下类:AtomicMarkableReference、AtomicStampedReference。第五类:关于高效的计数器的,CAS另一个缺点就是如果失败则自旋,会一直占用这CPU,所以这一类就是解决这个问题,主要包含如下类:LongAdder、LongAccumulator、DoubleAdder、DoubleAccumulator

这就是atomic包的全部类,他们都是Doug Lea大师设计的,如果真正理解,对接下来的lock包中的理解是有一定的帮助的,接下来我对每一类进行一个总结:

第一类:对基本变量和基本对象进行原子操作

对于单一基本变量boolean、int、long的原子操作都有对应的类,如果一个共享变量是这些基本变量,那么在多线程下对他们操作就有可能得不到正确的结果,这是由于Java内存模型所决定的,每一个线程都无法直接操作主内存中的数据,只能把主内存中的数据复制一份到自己的工作内存,然后对工作内存进行操作,至于什么时候刷新到主内存中,这个不确定,可能立即就刷新到主内存中,可能在某个适当的时候,所以导致每个线程都不可见,而AtomicBoolean、AtomicInteger、AtomicLong就是解决这些问题的,它通过关键字volatile来保证线程的可见性和有序性,通过CAS来保证线程的原子性,进而保证线程安全。而对于这种基本变量,CAS是通过比较值是否相等。

1:AtomicBoolean:原子更新boolean类型的值,底层是直接调用AtomicInteger来实现的2:AtomicInteger:原子更新int类型的值,CAS是调用Unsafe中compareAndSwapInt()方法对int类型原子操作3:AtomicLong:原子更新long类型的值,CAS是通过调用Unsafe中compareAndSwapLong()方法对long类型原子操作的。

对于单一的对象和基本变量的原理相同,不同的是AtomicReference通过比较对象的内存地址是否相同,与对象中的成员变量是否修改没有关系。

4:AtomicReference:原子更新引用类型,CAS是通过调用Unsafe中compareAndSwapObject()方法对对象原子操作的。

第一篇文章:你需要知道的AtomicInteger

第二篇文章:一起来了解AtomicReference

第二类:对数组中的元素进行原子操作

刚接触这一类的初学者来说,由于对其中的原理不太理解,他们总以为是对数组进行的原子操作,其实这是不对的,从源码中我们也可以看出底层的数组被final修饰,它是不可以变的,所有的方法都是对数组元素进行的操作,所以所有的原子操作针对的是数组中的元素,而非数组的本身。

5:AtomicIntegerArray:原子更新int类型数组中的元素,而非数组本身6:AtomicLongArray:原子更新long类型数组中的元素,而非数组本身7:AtomicReferenceArray:原子更新引用类型数组中的元素,而非数组本身。

第三篇文章:你真的了解AtomicIntegerArray吗?

第三类:对对象中被volatile修饰的成员变量进行原子操作

我在讲解这一类的时候也说了,这一类有很多限制,在实际的使用中好像用的不多,主要有三类,一个是对对象中int类型的成员变量原子操作,一个是对对象中long类型的成员变量原子操作,一个是对对象中引用类型的成员变量原子操作。

8:AtomicIntegerFieldUpdater:原子更新对象中int类型的成员变量9:AtomicLongFieldUpdater:原子更新对象中long类型的成员变量10:AtomicReferenceFieldUpdater:原子更新对象中的引用类型的成员变量

第四篇文章:AtomicIntegerFieldUpdater没有你想象的那么难

第四类:CAS中ABA解决方案

ABA问题是CAS中常见的问题,在一些场景中虽然有这个问题,但是对最终的结果并没有什么影响,但是在一些特殊的场景中就会出现问题,例如在转账的情况下,如果张三账户中有A元,张三的妻子取了N元后,账户中目前有B元,张三的父亲有存了N元后,账户中目前有A元,然后张三在存钱或者取钱时并不知道这种变化。可能这里例子不太合适,但是能够为我们理解ABA问题提供帮助,JDK为了解决ABA的问题,给我们提供了两个类

11:AtomicMarkableReference:通过一个boolean类型的标记来记录发生的变化。12:AtomicStampedReference:通过一个int类型的标记记录繁盛的变化,它就像一个戳,每一次变化都有一个标记。

第五篇文章:ABA的解决方案

第五类:解决CAS失败后重试占用CPU高的解决方案

在低并发是如果需要记录一个计数器,我们完全可以利用AtomicLong,但是它有一个缺点,如果CAS更新失败,那它就不停的重试,直到成功才停止,在并发量不太高的情况下性能还可以,但是如果在高并发下,CAS的失败重试次数就会直线上升,导致CPU使用也直线上升,所以为了解决CAS机制这一个问题。但是利用这个计算的并不是太准确,它只是一个大概值。它的大概原理:在没有并发时或者在初始化Cell数组时,通过更新被volatile修饰的base变量记录,如果存在并发则操作Cell数组,而Cell数组是通过锁分离技术实现的。

12:LongAdder:主要在原来基础上加上或者减去一个数13:LongAccumulate:主要在原来基础上和我们自定义的功能接口函数进行操作14:DoubleAdder:主要对double类型加上或者减去一个double类型的数15:DoubleAccumulate:主要对double类型和我们自定义的功能接口函数进行操作

第六篇文章:高性能计数器解决方案

标签: #高并发cas