`
ftj20003
  • 浏览: 130473 次
  • 性别: Icon_minigender_1
  • 来自: ...
社区版块
存档分类
最新评论

关于atomic问题的一点理解

    博客分类:
  • Java
阅读更多
    之前看到一个帖子是关于atomic使用的,当时没有仔细分析问题,理解有误,作者回信给我,我又去看了一下.原来的问题大致如下:
private AtomicLong longValue = new AtomicLong(0);
if(longValue.incrementAndGet() > 50) {
     longValue = new AtomicLong(0);
}

这样的写法能保证同步吗?按照这个写法的话,if块一定要使用同步锁保证同步的.但是那样的话Atomic的优势将大打折扣,代码的主要问题不在if而在if内的那句赋值:longValue = new AtomicLong(0).
    重新new出Atomic实例+同步锁的双重成本,可能会使代码执行的性能不尽如人意.所以我昨天想了一下,要保证同步又不使用锁其实利用Atomic的CAS应该是能做到的.写了一个小例子:
public class AtomicCounterSample {
    private final AtomicLong longValue = new AtomicLong(0);

    public void incrementOrClear() {

//      String name = Thread.currentThread().getName();

        long var = longValue.get();
        if(var >= 50) {
            longValue.compareAndSet(var,0); // 1
        }
        else {
//          System.out.println(name + ": " + var);

            longValue.compareAndSet(var,var + 1l); // 2
        }
    }

    public static void main(String[] args) {
        final AtomicCounterSample sample = new AtomicCounterSample();

        Thread threada = new Thread(new Runnable() {
            public void run() {
                for(int i = 0; i < 100; i++) {
                    sample.incrementOrClear();
                }
            }
        });
        threada.setName("A");

        Thread threadb = new Thread(new Runnable() {
            public void run() {
                for(int i = 0; i < 150; i++) {
                    sample.incrementOrClear();
                }
            }
        });
        threadb.setName("B");

        threada.start();
        threadb.start();
    }
}

这里我把AtomicLong的实例定义成final的,避免了实例化的开销.incrementOrClear()方法的作用就是递增或者清零.首先从理论上的保证是所有对AtomicLong的实例longValue的操作都是CAS的,所以其不会出现重复赋值的现象.这里我开了两个测试线程,循环调用incrementOrClear()方法,两个线程在方法内1,2处的情况分析如下:

    1.线程A,B都处于2处,有两个可能:(1)线程A,B的局部变量var相同,这时候有一个线程的CAS成功,另一个的var就会与longValue的最新值不同,CAS失败,保证了不重复写.(2)线程A,B的var不同,例如线程A的var=7之后A暂缓,B线程一直CAS直到其var=30时A继续执行,A,B同时进入2.这个时候线程A的CAS显然要失败,因为longValue的最新值是30,而B的CAS成功,保证了longValue的递增,同时A再次调用方法时,var的值被同步到最新的值.

    2.线程A,B都处于1处,这是只有一种可能就是两者的var都是50,此时也是只有一个线程能够成功的执行longValue的CAS操作使其清零.

    3.线程A,B分布在1,2两处.那么拥有与longValue的最新值一致的var会成功执行CAS操作.假设A在1处,B在2处.A的var=50,B的var=49.若longValue的最新值是50则清零成功.若longValue的最新值是49,这种情况是A的var=50时被暂缓了,B执行了清零并且新循环到49.这个时候依然是B的CAS成功,A的清零失败.即使此时B被暂缓了,那么A也会失败之后再次调用方法更新var的值替代B执行递增.如果恰好循环到B暂缓时的var值时B也继续执行了,那么就是分析1的情况(1).

    所以上述的分析,我认为任何情况都包括在内了,能够保证无锁机制的同步.并且实际运行也能体现了特别是3的分析:线程的,与longValue最新值不同的,var值再次打印时会被刷新而不是递增破坏longValue的唯一性.通过这个机会再次认识Atomic的CAS,我想还是有必要记录一下的.在此感谢作者的回复.
分享到:
评论
发表评论

文章已被作者锁定,不允许评论。

相关推荐

Global site tag (gtag.js) - Google Analytics