AtomicInteger 源码 及部分问题

  今天看了下传说中线程安全的AtomicInteger的源码,总共只有不到300行,其中关键的代码就更少了,主要有以下几段:安全

  1.value值设为volatile,保证其内存可见性ide

 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值,而后在各自的工做内存中执行了++操做,再往主存中同步的时候,就会写入一样的值,至关于一次++操做失效。this

  

  2.接着看源码,getAndSet(int newValue)方法spa

  /**
     * 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,效率要高的多。code

  3.getAndIncrement()方法blog

  /**
     * 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()方法rem

    /**
     * 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循环里面,不断的进行比较、更新操做,所以,能够想象,在线程数量很是大的状况下,运行速度会比较慢。

相关文章
相关标签/搜索