标签:
今天看了下传说中线程安全的AtomicInteger的源码,总共只有不到300行,其中关键的代码就更少了,主要有如下几段:
1.value值设为volatile,保证其内存可见性
private volatile int value;
值得注意的是,volatile关键字只是一种轻量级的同步方法,用来保证变量的内存可见性,但是并不能代替synchronizied。简单举个例子:
static volatile int count = 0; public static void main(String[] args) { for(int i=0;i<10000;i++){ new Thread(new Runnable() { @Override public void run() { count++; } }).start(); } System.out.println("count:"+count);
最终输出结果并不一定是期望的10000,而往往是一个比10000小的数字。原因是可能多个线程同时从主内存中读取到最新的conut值,然后在各自的工作内存中执行了++操作,再往主存中同步的时候,就会写入同样的值,相当于一次++操作失效。
2.接着看源码,getAndSet(int newValue)方法
/** * Atomically sets to the given value and returns the old value. * * @param newValue the new value * @return the previous value */ public final int getAndSet(int newValue) { for (;;) { int current = get(); if (compareAndSet(current, newValue)) return current; } }
方法中并没有synchronizied或者volatile关键字,那究竟是如何实现线程安全的呢?关键的方法在于compareAndSet(current,newValue),源码如下:
public final boolean compareAndSet(int expect, int update) { return unsafe.compareAndSwapInt(this, valueOffset, expect, update); }
调用了unsafe的compareAndSwapInt方法,是利用了大名鼎鼎的CAS来实现的原子操作,虽然CAS也用到了排他锁,但是比起synchronized,效率要高的多。
3.getAndIncrement()方法
/** * Atomically increments by one the current value. * * @return the previous value */ public final int getAndIncrement() { for (;;) { int current = get(); int next = current + 1; if (compareAndSet(current, next)) return current; } }
可见跟getAndSet方法大同小异,首先get()取出当前的value值,然后+1,紧接着还是调用了compareAndSet方法,看next和预期值是否相同,不相同则进入下一轮循环。
4.incrementAndGet()方法
/** * Atomically increments by one the current value. * * @return the updated value */ public final int incrementAndGet() { for (;;) { int current = get(); int next = current + 1; if (compareAndSet(current, next)) return next; } }
与getAndIncrement相比,只是最后一行代码有差别,一个是返回current,一个是返回next,个人理解就是利用CAS的原子操作,实现了线程安全的 i++和 ++i 。
其他的方法也都是类似的,到底层调用unsafe包的CAS原子操作实现,不多说了,下面是做了一个测试:
public static void main(String[] args) { final AtomicInteger integer = new AtomicInteger(0); for (int i = 0; i < 10000; i++) { new Thread(new Runnable() { @Override public void run() { integer.getAndIncrement(); } }).start(); } System.out.println(integer.get());
运行,输出10000没问题,再运行几遍,9999,9998都出现了,什么情况?说好的线程安全呢?百思不得其解,最后求助大神,给出答案,原来循环中的线程还没有执行完,就执行了打印的语句。修改代码如下:
public static void main(String[] args) { final AtomicInteger integer = new AtomicInteger(0); for (int i = 0; i < 10000; i++) { new Thread(new Runnable() { @Override public void run() { integer.getAndIncrement(); } }).start(); } try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(integer.get()); }
OK,再也没有出现之前的情况,可见AtomicInteger确实是能保证线程安全的,不过由于是一个无限的for循环里面,不断的进行比较、更新操作,因此,可以想象,在线程数量非常大的情况下,运行速度会比较慢。
标签:
原文地址:http://www.cnblogs.com/ljy201992/p/4828477.html