zoukankan      html  css  js  c++  java
  • J.U.C atomic AtomicInteger解析

          很多情况下我们只是需要简单的,高效,线程安全的递增递减方法。注意,这里有三个条件:简单,意味着程序员尽可能少的底层或者实现起来比较简单;高效,意味着耗用资源要少,程序处理速度要快; 线程安全也非常重要,这个在多线程下能保证数据的正确性。这三个条件看起来比较简单,但是实现起来却难以令人满意。

          通常情况下,在Java里面, ++i或者-- i不是线程安全的,这里面有三个独立的操作:读取变量当前值,为该值+1 /-1,然后写会新的值。在没有额外资源可以利用的情况下,只能使用加锁才能保证读-改-写这三个操作证的‘"原子性"。

         在J.U.C(Doug Lea)为加入jdk之前,是采用纯Java实现的,于是不可避免的采用了synchronized关键字。

    public final synchronized void set(int newValue);
    
    public final synchronized int getAndSet(int newValue);
    
    public final synchronized int incrementAndGet();

         同时在变量上使用了volatile(后面详述)来保证get()的时候不用加锁。尽管synchronized的代价还是很高,但是在没有JNI的手段下纯Java语言还是不能实现此操作的。

         来看看java.util.cocurrent.atomic.AtomicInteger开始:

         

    int addAndGet(int delta);
         //以原子方式将给定值与当前值想家。实际上就是等于线程安全版本的 i += delta
    
    boolean compareAndSet(int expect, int update);
        //如果当前值 == 预期值,则以原子方式将该值设置为给定的更新值。如果
        //更新成功就返回true, 否则就返回false, 并且不修改原值
    
    int decrementAndGet();
        //以原子方式将当前值减1,相当于线程安全版本的--i操作。
    
    int get(); //获得当前值。
    
    int getAndAdd(int delta);
         //以原子方式将给定值与当前值相加,相当于线程版本的 t = i;  i +=    //delat; return t;
    
    int getAndDecrement();
        //以原子方式将当前值减1,相当于线程安全版本的i--;
    
    int getAndIncrement();
       //以原子方式将当前值加1,相当于线程安全版本的i++;
    
    int getAndSet();
      //以原子方式设定给定值,并返回旧值;
    
    int incrementAndGet();
       //以原子方式将当前值加1,相当于线程安全版本的++i;

           看几个实现:

      private static final Unsafe unsafe = Unsafe.getUnsafe();
        private static final long valueOffset;
    
        static {
          try {
            valueOffset = unsafe.objectFieldOffset
                (AtomicInteger.class.getDeclaredField("value"));
          } catch (Exception ex) { throw new Error(ex); }
        }
    
        private volatile int value;

         调用sun.misc.Unsafe类实现cas操作(历史版权原因,Unsafe源码看不了),获取该对象value成员的内存地址;

      public final boolean compareAndSet(int expect, int update) {
            return unsafe.compareAndSwapInt(this, valueOffset, expect, update); //根据unsafe的cas操作,返回true or false
        }

         看一下上面API的实现:

    public final int getAndIncrement() {
            for (;;) {
                int current = get();
                int next = current + 1;
                if (compareAndSet(current, next))
                    return current;
            }
        }
    
       
        public final int getAndDecrement() {
            for (;;) {
                int current = get();
                int next = current - 1;
                if (compareAndSet(current, next))
                    return current;
            }
        }
    
     
        public final int getAndAdd(int delta) {
            for (;;) {
                int current = get();
                int next = current + delta;
                if (compareAndSet(current, next))
                    return current;
            }
        }
    
    
        public final int incrementAndGet() {
            for (;;) {
                int current = get();
                int next = current + 1;
                if (compareAndSet(current, next))
                    return next;
            }
        }

           方法实现总体差不多,for(;;)死循环,读取当前值,根据相应的加减cas操作,成功从for中返回,失败不同重试;

           测试用例如下:

    import java.util.concurrent.atomic.AtomicInteger;
    
    import org.junit.Test;
    
    import static org.junit.Assert.*;
    public class AtomicIntegerTest {
     
        @Test
        public void testAll() throws InterruptedException {
            final AtomicInteger value = new AtomicInteger(10);
            assertEquals(value.compareAndSet(1, 2), false);
            assertEquals(value.get(), 10);
            assertTrue(value.compareAndSet(10, 3));
            assertEquals(value.get(), 3);
            value.set(0);
            
            //
            assertEquals(value.incrementAndGet(), 1);
            assertEquals(value.getAndAdd(2), 1);
            assertEquals(value.getAndSet(5), 3);
            assertEquals(value.get(), 5);
            
            // final
            final int threadSize = 5;
            Thread[] threads = new Thread[threadSize];
            for (int i = 0; i < threadSize; i++) {
                threads[i] = new Thread() {
                    public void run() {
                    value.incrementAndGet();    
                    }
                };
            }
            for (Thread t : threads) {
                t.start();
            }
            
            for (Thread t : threads) {
                t.join();
            }
            //
            assertEquals(value.get(), 5 + threadSize);
        }
    }

          原文来至:http://www.blogjava.net/xylz/archive/2010/07/08/325587.html

        

  • 相关阅读:
    Ucloud的自主研发的检测主机是否被入侵的agent
    logstash 中多行合并
    python yield的解释
    influxdb 配置文件注释
    supervisor 完整安装步骤
    Linux创建系统用户
    kafka 集群的部署安装
    shell 计算时间差
    phantomjs 的安装部署
    yarn 的安装
  • 原文地址:https://www.cnblogs.com/onlysun/p/4616526.html
Copyright © 2011-2022 走看看