2018-8-19

  昨天看到java.util.concurrent.atomic相关的文章,之前有过留意但并未去了解,正好有空学习一下。本人理解atomic包是concurrent子包,当是为并发所用,拿int类型来说,

int i=0; i++; i++并没有做同步写处理,当并发去写时,就可能出现一个线程所写的结果被另外线程所写的结果覆盖,造成最终结果不符合预期,但是如果用synchronized修饰方法,

就是同步方法,与并发处理相冲突,atomic中的类型则可解决此问题,并发时,可理解为各线程尝试修改,不符合预期结果则更新数据重新尝试修改,可避免结果被覆盖情况。

  即可理解为synchronized严格同步为悲观锁,atomic为乐观锁,atomic中的类适合并发编程

以代码来展示:

public class AtomicTest {

    
    public static int clientTotal=10000;
    public static int threadTotal=200;
    public static int count=0;
    public static AtomicInteger atomicCount=new AtomicInteger(0);
    
    public static void main(String[] args) {
        try {
            test();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void test() throws InterruptedException {
        ExecutorService pool = Executors.newCachedThreadPool();
        final Semaphore semphore= new Semaphore(threadTotal);
        final CountDownLatch latch=new CountDownLatch(clientTotal);
        for(int i=0;i<clientTotal;i++) {
            pool.execute(()->{
                try {
                    semphore.acquire();
                    add();
                    addAtomic();
                    semphore.release();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                latch.countDown();
            });
        }
        latch.await();
        pool.shutdown();
        System.out.println("count--"+count);
        System.out.println("atomicCount--"+atomicCount);
    }
    public static void add() {
        count++;
    }
    public static void addAtomic() {
        atomicCount.incrementAndGet();
    }
    
}
View Code

结果:

count--9998
atomicCount--10000

结果中看出,count不符合预期,部分计算值被覆盖,比如执行一段时间后,当count=1300,线程thread5与线程thread6同时读到这个值,thread5运算写入count=1301,

thread6由于执行运算时count不是最新值,也随之写入count=1301,此时thread6把thread5结果覆盖了,本来两个线程执行完成预期是1302,真实完成后却是1301,

而atomicCount则符合预期。

查看AtomicInteger源码:

private volatile int value;
public final int incrementAndGet() {
        return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
    }
public final int getAndAddInt(Object o, long offset, int delta) {
        int v;
        do {
            v = getIntVolatile(o, offset);
        } while (!compareAndSwapInt(o, offset, v, v + delta));
        return v;
    }  
View Code

从代码中可看出实现原理,private volatile int value;用volatile修饰value,这样每次写读写都是用最新的值,以下代码

do { 

  v = getIntVolatile(o, offset);

} while (!compareAndSwapInt(o, offset, v, v + delta));

即先获得最新的value值,然后比较运算后是否符合预期,符合则写入,不符合则返回false,又重新尝试,compareAndSwap即熟知的CAS,此为原子操作,

即用volatile修饰符来保证最新值,用compareAndSwap方法来保证原子性。