码迷,mamicode.com
首页 > 其他好文 > 详细

AtomicInteger 源码 及部分问题

时间:2015-09-22 12:46:44      阅读:208      评论:0      收藏:0      [点我收藏+]

标签:

  今天看了下传说中线程安全的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循环里面,不断的进行比较、更新操作,因此,可以想象,在线程数量非常大的情况下,运行速度会比较慢。

  

AtomicInteger 源码 及部分问题

标签:

原文地址:http://www.cnblogs.com/ljy201992/p/4828477.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!