在java6之后咱们不但接触到了Lock相关的锁,也接触到了不少更加乐观的原子修改操做,也就是在修改时咱们只须要保证它的那个瞬间是安全的便可,通过相应的包装后能够再处理对象的并发修改,以及并发中的ABA问题,本文讲述Atomic系列的类的实现以及使用方法,其中包含:java
基本类:AtomicInteger、AtomicLong、AtomicBoolean;数据库
引用类型:AtomicReference、AtomicReference的ABA实例、AtomicStampedRerence、AtomicMarkableReference;数组
数组类型:AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray安全
属性原子修改器(Updater):AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicReferenceFieldUpdater并发
看到这么多类,你是否以为很困惑,其实没什么,由于你只须要看懂一个,其他的方法和使用都是大同小异的,相关的类会介绍他们之间的区别在哪里,在使用中须要注意的地方便可。app
在使用Atomic系列前,咱们须要先知道一个东西就是Unsafe类,全名为:sun.misc.Unsafe,这个类包含了大量的对C代码的操做,包括不少直接内存分配以及原子操做的调用,而它之因此标记为非安全的,是告诉你这个里面大量的方法调用都会存在安全隐患,须要当心使用,不然会致使严重的后果,例如在经过unsafe分配内存的时候,若是本身指定某些区域可能会致使一些相似C++同样的指针越界到其余进程的问题,不过它的具体使用并非本文的重点,本文重点是Atomic系列的内容大多会基于unsafe类中的如下几个本地方法来操做:dom
对象的引用进行对比后交换,交换成功返回true,交换失败返回false,这个交换过程彻底是原子的,在CPU上计算完结果后,都会对比内存的结果是否仍是原先的值,若不是,则认为不能替换,由于变量是volatile类型因此最终写入的数据会被其余线程看到,因此一个线程修改为功后,其余线程就发现本身修改失败了。测试
参数1:对象所在的类自己的对象(通常这里是对一个对象的属性作修改,才会出现并发,因此该对象所存在的类也是有一个对象的)this
参数2:这个属性在这个对象里面的相对便宜量位置,其实对比时是对比内存单元,因此须要属性的起始位置,而引用就是修改引用地址(根据OS、VM位数和参数配置决定宽度通常是4-8个字节),int就是修改相关的4个字节,而long就是修改相关的8个字节。atom
获取偏移量也是经过unsafe的一个方法:objectFieldOffset(Fieldfield)来获取属性在对象中的偏移量;静态变量须要经过:staticFieldOffset(Field field)获取,调用的总方法是:fieldOffset(Fieldfield)
参数3:修改的引用的原始值,用于对比原来的引用和要修改的目标是否一致。
参数4:修改的目标值,要将数据修改为什么。
[java] view plain copy


- public final native boolean compareAndSwapObject(Object paramObject1, long paramLong, Object paramObject2, Object paramObject3);
-
- public final native boolean compareAndSwapInt(Object paramObject, long paramLong, int paramInt1, int paramInt2);
#对long的操做,要看VM是否支持对Long的CAS,由于有可能VM自己不支持,若不支持,此时运算会变成Lock方式,不过如今VM都基本是支持的而已。
[java] view plain copy


- public final native boolean compareAndSwapLong(Object paramObject, long paramLong1, long paramLong2, long paramLong3);
咱们不推荐直接使用unsafe来操做原子变量,而是经过java封装好的一些类来操做原子变量。
实例代码1:AtomicIntegerTest.java
[java] view plain copy


- import java.util.concurrent.atomic.AtomicInteger;
- public class AtomicIntegerTest {
-
- /**
- * 常见的方法列表
- * @see AtomicInteger#get() 直接返回值
- * @see AtomicInteger#getAndAdd(int) 增长指定的数据,返回变化前的数据
- * @see AtomicInteger#getAndDecrement() 减小1,返回减小前的数据
- * @see AtomicInteger#getAndIncrement() 增长1,返回增长前的数据
- * @see AtomicInteger#getAndSet(int) 设置指定的数据,返回设置前的数据
- *
- * @see AtomicInteger#addAndGet(int) 增长指定的数据后返回增长后的数据
- * @see AtomicInteger#decrementAndGet() 减小1,返回减小后的值
- * @see AtomicInteger#incrementAndGet() 增长1,返回增长后的值
- * @see AtomicInteger#lazySet(int) 仅仅当get时才会set
- *
- * @see AtomicInteger#compareAndSet(int, int) 尝试新增后对比,若增长成功则返回true不然返回false
- */
- public final static AtomicInteger TEST_INTEGER = new AtomicInteger(1);
-
- public static void main(String []args) throws InterruptedException {
- final Thread []threads = new Thread[10];
- for(int i = 0 ; i < 10 ; i++) {
- final int num = i;
- threads[i] = new Thread() {
- public void run() {
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- int now = TEST_INTEGER.incrementAndGet();
- System.out.println("我是线程:" + num + ",我获得值了,增长后的值为:" + now);
- }
- };
- threads[i].start();
- }
- for(Thread t : threads) {
- t.join();
- }
- System.out.println("最终运行结果:" + TEST_INTEGER.get());
- }
- }<strong>
- </strong>
代码例子中模拟多个线程并发对AtomicInteger进行增长1的操做,若是这个数据是普通类型,那么增长过程当中出现的问题就是两个线程可能同时看到的数据都是同一个数据,增长完成后写回的时候,也是同一个数据,可是两个加法应当串行增长1,也就是加2的操做,甚至于更加特殊的状况是一个线程加到3后,写入,另外一个线程写入了2,还越变越少,也就是不能获得正确的结果,在并发下,咱们模拟计数器,要获得精确的计数器值,就须要使用它,咱们但愿获得的结果是11,能够拷贝代码进去运行后看到结果的确是11,顺然输出的顺序可能不同,也同时能够证实线程的确是并发运行的(只是在输出的时候,征用System.out这个对象也不必定是谁先抢到),可是最终结果的确是11。
相信你对AtomicInteger的使用有一些了解了吧,要知道更多的方法使用,请参看这段代码中定义变量位置的注释,有关于AtomicInteger的相关方法的详细注释,能够直接跟踪进去看源码,注释中使用了简单的描述说明了方法的用途。
而对于AtomicLong呢,其实和AtomicInteger差很少,惟一的区别就是它处理的数据是long类型的就是了;
对于AtomicBoolean呢,方法要少一些,常见的方法就两个:
[java] view plain copy


- AtomicBoolean#compareAndSet(boolean, boolean) 第一个参数为原始值,第二个参数为要修改的新值,若修改为功则返回true,不然返回false
- AtomicBoolean#getAndSet(boolean) 尝试设置新的boolean值,直到成功为止,返回设置前的数据
由于boolean值就两个值,因此就是来回改,相对的不少增长减小的方法天然就没有了,对于使用来说,咱们列举一个boolean的并发修改,仅有一个线程能够修改为功的例子:
实例代码2:AtomicBooleanTest.java
[java] view plain copy


- import java.util.concurrent.atomic.AtomicBoolean;
-
- public class AtomicBooleanTest {
-
- /**
- * 主要方法:
- * @see AtomicBoolean#compareAndSet(boolean, boolean) 第一个参数为原始值,第二个参数为要修改的新值,若修改为功则返回true,不然返回false
- * @see AtomicBoolean#getAndSet(boolean) 尝试设置新的boolean值,直到成功为止,返回设置前的数据
- */
- public final static AtomicBoolean TEST_BOOLEAN = new AtomicBoolean();
-
- public static void main(String []args) {
- for(int i = 0 ; i < 10 ; i++) {
- new Thread() {
- public void run() {
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- if(TEST_BOOLEAN.compareAndSet(false, true)) {
- System.out.println("我成功了!");
- }
- }
- }.start();
- }
- }
- }
这里有10个线程,咱们让他们几乎同时去征用boolean值的修改,修改为功者输出:我成功了!此时你运行完你会发现只会输出一个“我成功了!”,说明征用过程当中达到了锁的效果。
那么几种基本类型就说完了,咱们来看看里面的实现是否是如咱们开始说的Unsafe那样,看几段源码便可,咱们看下AtomicInteger的一些源码,例如开始用的:incrementAndGet方法,这个,它的源码是:
[java] view plain copy


- public final int incrementAndGet() {
- for (;;) {
- int current = get();
- int next = current + 1;
- if (compareAndSet(current, next))
- return next;
- }
- }
能够看到内部有一个死循环,只有不断去作compareAndSet操做,直到成功为止,也就是修改的根本在compareAndSet方法里面,能够去看下相关的修改方法均是这样实现,那么看下compareAndSet方法的body部分是:
[java] view plain copy


- public final boolean compareAndSet(int expect, int update) {
- return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
- }
能够看到这里使用了unsafe的compareAndSwapInt的方法,很明显this就是指AtomicInteger当前的这个对象(这个对象不用像上面说的它不能是static和final,它无所谓的),而valueOffset的定义是这样的:
[java] view plain copy


- private static final long valueOffset;
-
- static {
- try {
- valueOffset = unsafe.objectFieldOffset
- (AtomicInteger.class.getDeclaredField("value"));
- } catch (Exception ex) {
- throw new Error(ex); }
- }
能够看出是经过咱们前面所述的objectFieldOffset方法来获取的属性偏移量,因此你本身若是定义相似的操做的时候,就要注意,这个属性不能是静态的,不然不能用这个方法来获取。
后面两个参数天然是对比值和须要修改的目标对象的地址。
其实Atomic系列你看到这里,java层面你就知道差很少了,其他的就是特殊用法和包装而已,刚才咱们说了unsafe的3个方法无非是地址和值的区别在内存层面是没有本质区别的,由于地址自己也是数字值。
为了说明这个问题,咱们就先说Reference的使用:
咱们测试一个reference,和boolean测试方式同样,也是测试多个线程只有一个线程能修改它。
实例代码1:AtomicReferenceTest.java
[java] view plain copy


- import java.util.concurrent.atomic.AtomicReference;
-
- public class AtomicReferenceTest {
-
- /**
- * 相关方法列表
- * @see AtomicReference#compareAndSet(Object, Object) 对比设置值,参数1:原始值,参数2:修改目标引用
- * @see AtomicReference#getAndSet(Object) 将引用的目标修改成设置的参数,直到修改为功为止,返回修改前的引用
- */
- public final static AtomicReference <String>ATOMIC_REFERENCE = new AtomicReference<String>("abc");
-
- public static void main(String []args) {
- for(int i = 0 ; i < 100 ; i++) {
- final int num = i;
- new Thread() {
- public void run() {
- try {
- Thread.sleep(Math.abs((int)(Math.random() * 100)));
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- if(ATOMIC_REFERENCE.compareAndSet("abc", new String("abc"))) {
- System.out.println("我是线程:" + num + ",我得到了锁进行了对象修改!");
- }
- }
- }.start();
- }
- }
- }
测试结果如咱们所料,的确只有一个线程,执行,跟着代码:compareAndSet进去,发现源码中的调用是:
[java] view plain copy


- public final boolean compareAndSet(V expect, V update) {
- return unsafe.compareAndSwapObject(this, valueOffset, expect, update);
- }
OK,的确和咱们上面所讲一致,那么此时咱们又遇到了引用修改的新问题,什么问题呢?ABA问题,什么是ABA问题呢,当某些流程在处理过程当中是顺向的,也就是不容许重复处理的状况下,在某些状况下致使一个数据由A变成B,再中间可能通过0-N个环节后变成了A,此时A不容许再变成B了,由于此时的状态已经发生了改变,例如:银行资金里面作一批帐目操做,要求资金在80-100元的人,增长20元钱,时间持续一天,也就是后台程序会不断扫描这些用户的资金是不是在这个范围,可是要求增长过的人就不能再增长了,若是增长20后,被人取出10元继续在这个范围,那么就能够无限套现出来,就是ABA问题了,相似的还有抢红包或中奖,好比天天每一个人限量3个红包,中那个等级的奖的个数等等。
此时咱们须要使用的方式就不是简单的compareAndSet操做,由于它仅仅是考虑到物理上的并发,而不是在业务逻辑上去控制顺序,此时咱们须要借鉴数据库的事务序列号的一些思想来解决,假如每一个对象修改的次数能够记住,修改前先对比下次数是否一致再修改,那么这个问题就简单了,AtomicStampedReference类正是提供这一功能的,其实它仅仅是在AtomicReference类的再一次包装,里面增长了一层引用和计数器,实际上是否为计数器彻底由本身控制,大多数咱们是让他自增的,你也能够按照本身的方式来标示版本号,下面一个例子是ABA问题的简单演示:
实例代码3(ABA问题模拟代码演示):
[java] view plain copy


- import java.util.concurrent.atomic.AtomicReference;
-
- /**
- * ABA问题模拟,线程并发中,致使ABA问题,解决方案是使用|AtomicMarkableReference
- * 请参看相应的例子:AtomicStampedReferenceTest、AtomicMarkableReferenceTest
- *
- */
- public class AtomicReferenceABATest {
-
- public final static AtomicReference <String>ATOMIC_REFERENCE = new AtomicReference<String>("abc");
-
- public static void main(String []args) {
- for(int i = 0 ; i < 100 ; i++) {
- final int num = i;
- new Thread() {
- public void run() {
- try {
- Thread.sleep(Math.abs((int)(Math.random() * 100)));
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- if(ATOMIC_REFERENCE.compareAndSet("abc" , "abc2")) {
- System.out.println("我是线程:" + num + ",我得到了锁进行了对象修改!");
- }
- }
- }.start();
- }
- new Thread() {
- public void run() {
- while(!ATOMIC_REFERENCE.compareAndSet("abc2", "abc"));
- System.out.println("已经改成原始值!");
- }
- }.start();
- }
- }<strong>
- </strong>
代码中和原来的例子,惟一的区别就是最后增长了一个线程让他将数据修改成原来的值,并一直尝试修改,直到修改为功为止,为何没有直接用:方法呢getAndSet方法呢,由于咱们的目的是要让某个线程先将他修改成abc2后再让他修改回abc,因此须要这样作;
此时咱们获得的结果是:
我是线程:41,我得到了锁进行了对象修改!
已经改成原始值!
我是线程:85,我得到了锁进行了对象修改!
固然你的线程编号多半和我不同,只要征用到就对,能够发现,有两个线程修改了这个字符串,咱们是想那一堆将abc改为abc2的线程仅有一个成功,即便其余线程在他们征用时将其修改成abc,也不能再修改。
此时咱们经过类来AtomicStampedReference解决这个问题:
实例代码4(AtomicStampedReference解决ABA问题):
[java] view plain copy


- import java.util.concurrent.atomic.AtomicStampedReference;
-
- public class AtomicStampedReferenceTest {
-
- public final static AtomicStampedReference <String>ATOMIC_REFERENCE = new AtomicStampedReference<String>("abc" , 0);
-
- public static void main(String []args) {
- for(int i = 0 ; i < 100 ; i++) {
- final int num = i;
- final int stamp = ATOMIC_REFERENCE.getStamp();
- new Thread() {
- public void run() {
- try {
- Thread.sleep(Math.abs((int)(Math.random() * 100)));
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- if(ATOMIC_REFERENCE.compareAndSet("abc" , "abc2" , stamp , stamp + 1)) {
- System.out.println("我是线程:" + num + ",我得到了锁进行了对象修改!");
- }
- }
- }.start();
- }
- new Thread() {
- public void run() {
- int stamp = ATOMIC_REFERENCE.getStamp();
- while(!ATOMIC_REFERENCE.compareAndSet("abc2", "abc" , stamp , stamp + 1));
- System.out.println("已经改回为原始值!");
- }
- }.start();
- }
- }
此时再运行程序看到的结果就是咱们想要的了,发现将abc修改成abc2的线程仅有一个被访问,虽然被修改回了原始值,可是其余线程也不会再将abc改成abc2。
而类:AtomicMarkableReference和AtomicStampedReference功能差很少,有点区别的是:它描述更加简单的是与否的关系,一般ABA问题只有两种状态,而AtomicStampedReference是多种状态,那么为何还要有AtomicMarkableReference呢,由于它在处理是与否上面更加具备可读性,而AtomicStampedReference过于随意定义状态,并不便于阅读大量的是和否的关系,它能够被认为是一个计数器或状态列表等信息,java提倡经过类名知道其意义,因此这个类的存在也是必要的,它的定义就是将数据变换为true|false以下:
[java] view plain copy


- public final static AtomicMarkableReference <String>ATOMIC_MARKABLE_REFERENCE = new AtomicMarkableReference<String>("abc" , false);
操做时使用:
[java] view plain copy


- ATOMIC_MARKABLE_REFERENCE.compareAndSet("abc", "abc2", false, true);
好了,reference的三个类的种类都介绍了,咱们下面要开始说Atomic的数组用法,由于咱们开始说到的都是一些简单变量和基本数据,操做数组呢?若是你来设计会怎么设计,Atomic的数组要求不容许修改长度等,不像集合类那么丰富的操做,不过它可让你的数组上每一个元素的操做绝对安全的,也就是它细化的力度仍是到数组上的元素,为你作了二次包装,因此若是你来设计,就是在原有的操做上增长一个下标访问便可,咱们来模拟一个Integer类型的数组,即:AtomicIntegerArray
实例代码5(AtomicIntegerArrayTest.java)
[java] view plain copy


- import java.util.concurrent.atomic.AtomicIntegerArray;
-
- public class AtomicIntegerArrayTest {
-
- /**
- * 常见的方法列表
- * @see AtomicIntegerArray#addAndGet(int, int) 执行加法,第一个参数为数组的下标,第二个参数为增长的数量,返回增长后的结果
- * @see AtomicIntegerArray#compareAndSet(int, int, int) 对比修改,参数1:数组下标,参数2:原始值,参数3,修改目标值,修改为功返回true不然false
- * @see AtomicIntegerArray#decrementAndGet(int) 参数为数组下标,将数组对应数字减小1,返回减小后的数据
- * @see AtomicIntegerArray#incrementAndGet(int) 参数为数组下标,将数组对应数字增长1,返回增长后的数据
- *
- * @see AtomicIntegerArray#getAndAdd(int, int) 和addAndGet相似,区别是返回值是变化前的数据
- * @see AtomicIntegerArray#getAndDecrement(int) 和decrementAndGet相似,区别是返回变化前的数据
- * @see AtomicIntegerArray#getAndIncrement(int) 和incrementAndGet相似,区别是返回变化前的数据
- * @see AtomicIntegerArray#getAndSet(int, int) 将对应下标的数字设置为指定值,第二个参数为设置的值,返回是变化前的数据
- */
- private final static AtomicIntegerArray ATOMIC_INTEGER_ARRAY = new AtomicIntegerArray(new int[]{1,2,3,4,5,6,7,8,9,10});
-
- public static void main(String []args) throws InterruptedException {
- Thread []threads = new Thread[100];
- for(int i = 0 ; i < 100 ; i++) {
- final int index = i % 10;
- final int threadNum = i;
- threads[i] = new Thread() {
- public void run() {
- int result = ATOMIC_INTEGER_ARRAY.addAndGet(index, index + 1);
- System.out.println("线程编号为:" + threadNum + " , 对应的原始值为:" + (index + 1) + ",增长后的结果为:" + result);
- }
- };
- threads[i].start();
- }
- for(Thread thread : threads) {
- thread.join();
- }
- System.out.println("=========================>\n执行已经完成,结果列表:");
- for(int i = 0 ; i < ATOMIC_INTEGER_ARRAY.length() ; i++) {
- System.out.println(ATOMIC_INTEGER_ARRAY.get(i));
- }
- }
- }
计算结果说明:100个线程并发,每10个线程会被并发修改数组中的一个元素,也就是数组中的每一个元素会被10个线程并发修改访问,每次增长原始值的大小,此时运算完的结果看最后输出的敲好为原始值的11倍数,和咱们预期的一致,若是不是线程安全那么这个值什么都有可能。
而相应的类:AtomicLongArray其实和AtomicIntegerArray操做方法相似,最大区别就是它操做的数据类型是long;而AtomicRerenceArray也是这样,只是它方法只有两个:
[java] view plain copy


- AtomicReferenceArray#compareAndSet(int, Object, Object)
- 参数1:数组下标;
- 参数2:修改原始值对比;
- 参数3:修改目标值
- 修改为功返回true,不然返回false
-
- AtomicReferenceArray#getAndSet(int, Object)
- 参数1:数组下标
- 参数2:修改的目标
- 修改为功为止,返回修改前的数据
到这里你是否对数组内部的操做应该有所了解了,和当初预期同样,参数就是多了一个下标,为了彻底验证这点,跟踪到源码中能够看到:
[java] view plain copy


- public final int addAndGet(int i, int delta) {
- while (true) {
- int current = get(i);
- int next = current + delta;
- if (compareAndSet(i, current, next))
- return next;
- }
- }
能够看到根据get(i)获取到对应的数据,而后作和普通AtomicInteger差很少的操做,get操做里面有个细节是:
[java] view plain copy


- public final int get(int i) {
- return unsafe.getIntVolatile(array, rawIndex(i));
- }
这里经过了unsafe获取基于volatile方式获取(可见性)获取一个int类型的数据,而获取的位置是由rawIndex来肯定,它的源码是:
[java] view plain copy


- private long rawIndex(int i) {
- if (i < 0 || i >= array.length)
- throw new IndexOutOfBoundsException("index " + i);
- return base + (long) i * scale;
- }
能够发现这个结果是一个地址位置,为base加上一耳光偏移量,那么看看base和scale的定义为:
[java] view plain copy


- private static final int base = unsafe.arrayBaseOffset(int[].class);
- private static final int scale = unsafe.arrayIndexScale(int[].class);
能够发现unsafe里面提供了对数组base的位置的获取,由于对象是有头部的,而数组还有一个长度位置,第二个很明显是一个数组元素所占用的宽度,也就是基本精度;这里应该能够体会到unsafe所带来的强大了吧。
本文最后要介绍的部分为Updater也就是修改器,它算是Atomic的系列的一个扩展,Atomic系列是为你定义好的一些对象,你可使用,可是若是是别人已经在使用的对象会原先的代码须要修改成Atomic系列,此时若所有修改类型到对应的对象相信很麻烦,由于牵涉的代码会不少,此时java提供一个外部的Updater能够对对象的属性自己的修改提供相似Atomic的操做,也就是它对这些普通的属性的操做是并发下安全的,分别由:AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicReferenceUpdater,这样操做后,系统会更加灵活,也就是可能那些类的属性只是在某些状况下须要控制并发,不少时候不须要,可是他们的使用一般有如下几个限制:
限制1:操做的目标不能是static类型,前面说到unsafe的已经能够猜想到它提取的是非static类型的属性偏移量,若是是static类型在获取时若是没有使用对应的方法是会报错的,而这个Updater并无使用对应的方法。
限制2:操做的目标不能是final类型的,由于final根本无法修改。
限制3:必须是volatile类型的数据,也就是数据自己是读一致的。
限制4:属性必须对当前的Updater所在的区域是可见的,也就是private若是不是当前类确定是不可见的,protected若是不存在父子关系也是不可见的,default若是不是在同一个package下也是不可见的。
实现方式:经过反射找到属性,对属性进行操做,可是并非设置accessable,因此必须是可见的属性才能操做。
说了这么多,来个实例看看吧。
实例代码6:(AtomicIntegerFieldUpdaterTest.java)
[java] view plain copy


- import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
-
- public class AtomicIntegerFieldUpdaterTest {
-
- static class A {
- volatile int intValue = 100;
- }
-
- /**
- * 能够直接访问对应的变量,进行修改和处理
- * 条件:要在可访问的区域内,若是是private或挎包访问default类型以及非父亲类的protected均没法访问到
- * 其次访问对象不能是static类型的变量(由于在计算属性的偏移量的时候没法计算),也不能是final类型的变量(由于根本没法修改),必须是普通的成员变量
- *
- * 方法(说明上和AtomicInteger几乎一致,惟一的区别是第一个参数须要传入对象的引用)
- * @see AtomicIntegerFieldUpdater#addAndGet(Object, int)
- * @see AtomicIntegerFieldUpdater#compareAndSet(Object, int, int)
- * @see AtomicIntegerFieldUpdater#decrementAndGet(Object)
- * @see AtomicIntegerFieldUpdater#incrementAndGet(Object)
- *
- * @see AtomicIntegerFieldUpdater#getAndAdd(Object, int)
- * @see AtomicIntegerFieldUpdater#getAndDecrement(Object)
- * @see AtomicIntegerFieldUpdater#getAndIncrement(Object)
- * @see AtomicIntegerFieldUpdater#getAndSet(Object, int)
- */
- public final static AtomicIntegerFieldUpdater <A>ATOMIC_INTEGER_UPDATER = AtomicIntegerFieldUpdater.newUpdater(A.class, "intValue");
-
- public static void main(String []args) {
- final A a = new A();
- for(int i = 0 ; i < 100 ; i++) {
- final int num = i;
- new Thread() {
- public void run() {
- if(ATOMIC_INTEGER_UPDATER.compareAndSet(a, 100, 120)) {
- System.out.println("我是线程:" + num + " 我对对应的值作了修改!");
- }
- }
- }.start();
- }
- }
- }
此时你会发现只有一个线程能够对这个数据进行修改,其余的方法如上面描述同样,实现的功能和AtomicInteger相似。
而AtomicLongFieldUpdater其实也是这样,区别在于它所操做的数据是long类型。
AtomicReferenceFieldUpdater方法较少,主要是compareAndSet以及getAndSet两个方法的使用,它的定义比数字类型的多一个参数以下:
[java] view plain copy


- static class A {
- volatile String stringValue = "abc";
- }
-
-
- AtomicReferenceFieldUpdater <A ,String>ATOMIC_REFERENCE_FIELD_UPDATER = AtomicReferenceFieldUpdater.newUpdater(A.class, String.class, "stringValue");
能够看到,这里传递的参数增长了一个属性的类型,由于引用的是一个对象,对象自己也有一个类型。