总结:
-
原子包提供了四种类型的原子类。包括基本数据类型,数组,引用类型,引用类型成员变量四种。
-
底层使用CAS操作保证原子性,CAS就是compare and swap先比较,如果是期望的值就进行交换。CAS是通过加总线锁或者使用内存锁定在硬件层面实现的。
-
CAS有一些缺点:1. 单个操作能够保证原子性,但是复合操作不能够保证。2. 并发量大的时候会导致大量的循环比较操作消耗性能。 3. ABA问题。会丢失掉一些中间处理过程。
-
解决ABA问题使用AtomicStampedReference解决,加了一个version版本号每次判断版本。
===简述
http://www.cnblogs.com/skywang12345/p/java_threads_category.html
atomic提供了原子操作的十二个类,四种类型。
使得能够对基本数据类型,数组,对象,和对象成员进行原子操作。
原子操作指的是操作过程不会被其他线程中断。
1. 基本类型: AtomicInteger, AtomicLong, AtomicBoolean ;
2. 数组类型: AtomicIntegerArray, AtomicLongArray, AtomicReferenceArray ;
3. 引用类型: AtomicReference, AtomicStampedRerence, AtomicMarkableReference ;
4. 对象的属性修改类型: AtomicIntegerFieldUpdater, AtomicLongFieldUpdater, AtomicReferenceFieldUpdater 。
===当涉及到更新数据的操作的时候,atomic类是如何保证原子性的呢?用的是什么类的什么方法呢?
底层使用volatile来定义类中的成员变量来实现可见性。cas操作来实现原子性。
底层使用到unsafe类的三个方法compareAndSwapObject,compareAndSwapInt和compareAndSwapLong,再看AtomicBoolean源码,发现其是先把Boolean转换成整型,再使用compareAndSwapInt进行CAS,所以原子更新double也可以用类似的思路来实现。
cas操作就是一个期望值,修改值,如果当前值等于期望值,就进行修改。
===cas操作如何保证线程安全呢?在硬件上具体是如何实现的呢?
cas方法在语言方面不做任何处理,不加锁,但是实现了硬件层面的阻塞。处理器使用基于对缓存加锁或总线加锁的方式来实现多处理器之间的原子操作。
第一个机制是通过总线锁保证原子性。。如果多个处理器同时对共享变量进行读改写(i++就是经典的读改写操作)操作,那么共享变量就会被多个处理器同时进行操作,这样读改写操作就不是原子的,操作完之后共享变量的值会和期望的不一致,举个例子:如果i=1,我们进行两次i++操作,我们期望的结果是3,但是有可能结果是2。
原因是有可能多个处理器同时从各自的缓存中读取变量i,分别进行加一操作,然后分别写入系统内存当中。那么想要保证读改写共享变量的操作是原子的,就必须保证CPU1读改写共享变量的时候,CPU2不能操作缓存了该共享变量内存地址的缓存。
处理器使用总线锁就是来解决这个问题的。所谓总线锁就是使用处理器提供的一个LOCK#信号,当一个处理器在总线上输出此信号时,其他处理器的请求将被阻塞住,那么该处理器可以独占使用共享内存。
第二个机制是通过缓存锁定保证原子性。在同一时刻我们只需保证对某个内存地址的操作是原子性即可,但总线锁定把CPU和内存之间通信锁住了,这使得锁定期间,其他处理器不能操作其他内存地址的数据,所以总线锁定的开销比较大,最近的处理器在某些场合下使用缓存锁定代替总线锁定来进行优化。
http://blog.csdn.net/a953713428/article/details/54562648
http://blog.csdn.net/goodlixueyong/article/details/51339689
CAS虽然很高效的实现了原子操作,但是它依然存在三个问题。
1、ABA问题。CAS在操作值的时候检查值是否已经变化,没有变化的情况下才会进行更新。但是如果一个值原来是A,变成B,又变成A,那么CAS进行检查时会认为这个值没有变化,但是实际上却变化了。ABA问题的解决方法是使用版本号。在变量前面追加上版本号,每次变量更新的时候把版本号加一,那么A-B-A 就变成1A-2B-3A。从Java1.5开始JDK的atomic包里提供了一个类AtomicStampedReference来解决ABA问题。加了一个版本号标识,判断版本号码是否一致,一致就交换。
2、并发越高,失败的次数会越多,CAS如果长时间不成功,会极大的增加CPU的开销。因此CAS不适合竞争十分频繁的场景。
3、只能保证一个共享变量的原子操作。当对多个共享变量操作时,CAS就无法保证操作的原子性,这时就可以用锁,或者把多个共享变量合并成一个共享变量来操作。比如有两个共享变量i=2,j=a,合并一下ij=2a,然后用CAS来操作ij。从Java1.5开始JDK提供了AtomicReference类来保证引用对象的原子性,你可以把多个变量放在一个对象里来进行CAS操作。
===AtomicIntegerFieldUpdater使用反射机制来进行对象成员变量的原子修改操作。同时要把接受修改的成员变量设置成volatile的类型
===LongAdder比起AtomicInteger来说有什么优势吗?
在高并发场景下LongAdder比AtomitInteger效率高很多。Atomic类主要是依靠CAS操作保证同步的,但是缺点就是因为是单点更新只有一个value值,所以高并发下CAS失败的几率很高,重复操作就很多,消耗性能。
LongAdder的优势就是在低并发场景下,使用和Atomic一样的CAS原子更新,但是在高并发场景下使用分点更新的手段。维护了一个Cell数组,cell里面有个value存值,高并发场景下线程分配到不同的Cell中去更新相应的value值,也就是分段更新操作,需要总数的时候加起来就可以了。