2018-8-19
昨天看到java.util.concurrent.atomic相关的文章,之前有过留意但并未去了解,正好有空学习一下。本人理解atomic包是concurrent子包,当是为并发所用,拿int类型来说,
int i=0; i++; i++并没有做同步写处理,当并发去写时,就可能出现一个线程所写的结果被另外线程所写的结果覆盖,造成最终结果不符合预期,但是如果用synchronized修饰方法,
就是同步方法,与并发处理相冲突,atomic中的类型则可解决此问题,并发时,可理解为各线程尝试修改,不符合预期结果则更新数据重新尝试修改,可避免结果被覆盖情况。
即可理解为synchronized严格同步为悲观锁,atomic为乐观锁,atomic中的类适合并发编程
以代码来展示:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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(); } }
结果:
count--9998
atomicCount--10000
结果中看出,count不符合预期,部分计算值被覆盖,比如执行一段时间后,当count=1300,线程thread5与线程thread6同时读到这个值,thread5运算写入count=1301,
thread6由于执行运算时count不是最新值,也随之写入count=1301,此时thread6把thread5结果覆盖了,本来两个线程执行完成预期是1302,真实完成后却是1301,
而atomicCount则符合预期。
查看AtomicInteger源码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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; }
从代码中可看出实现原理,private volatile int value;用volatile修饰value,这样每次写读写都是用最新的值,以下代码
do {
v = getIntVolatile(o, offset);
} while (!compareAndSwapInt(o, offset, v, v + delta));
即先获得最新的value值,然后比较运算后是否符合预期,符合则写入,不符合则返回false,又重新尝试,compareAndSwap即熟知的CAS,此为原子操作,
即用volatile修饰符来保证最新值,用compareAndSwap方法来保证原子性。