Java JUC之Atomic系列12大类实例讲解和原理分解(三)
w Error(ex); }
}
可以看出是通过我们前面所述的objectFieldOffset方法来获取的属性偏移量,所以你自己如果定义类似的操作的时候,就要注意,这个属性不能是静态的,否则不能用这个方法来获取。
后面两个参数自然是对比值和需要修改的目标对象的地址。
其实Atomic系列你看到这里,java层面你就知道差不多了,其余的就是特殊用法和包装而已,刚才我们说了unsafe的3个方法无非是地址和值的区别在内存层面是没有本质区别的,因为地址本身也是数字值。
为了说明这个问题,我们就先说Reference的使用:
我们测试一个reference,和boolean测试方式一样,也是测试多个线程只有一个线程能修改它。
实例代码1:AtomicReferenceTest.java
[java]
import java.util.concurrent.atomic.AtomicReference;
public class AtomicReferenceTest {
/**
* 相关方法列表
* @see AtomicReference#compareAndSet(Object, Object) 对比设置值,参数1:原始值,参数2:修改目标引用
* @see AtomicReference#getAndSet(Object) 将引用的目标修改为设置的参数,直到修改成功为止,返回修改前的引用
*/
public final static AtomicReference ATOMIC_REFERENCE = new AtomicReference("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]
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]
import java.util.concurrent.atomic.AtomicReference;
/**
* ABA问题模拟,线程并发中,导致ABA问题,解决方案是使用|AtomicMarkableReference
* 请参看相应的例子:AtomicStampedReferenceTest、AtomicMarkableReferenceTest
* @author zhongyin.xy
*
*/
public class AtomicReferenceABATest {
public final static AtomicReference ATOMIC_REFERENCE = new AtomicReference("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() {