深入理解java AtomicInteger原子类型

一、为什么要用到AtomicInteger?

当进行并发编程的时候我们需要保证线程安全,何为线程安全?

当多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些线程将如何交替执行,并且在主调代码中不需要任何额外的同步或协同,这个类都能表现出正确的行为,那么这个类就是线程安全的。

当我门需要用一个变量来计数的时候,我们首先想到的是 count++,但是这个自增操作并不是线程安全的。

count++可以分为3步:

    1.获取count当前的值;

    2.给获取的count当前值加1

    3.写回新的值到变量

由此可见,count++并不是原子操作,假设count的初始值为10,当进行并发操作的时候,可能出现线程A和线程B都进行到了1操作,之后又同时进行2操作。A先进行到3操作+1,现在值为11;注意刚才AB获取到的当前值都是10,所以B执行3操作后,count的值依然是11。这个结果显然不符合我们的要求。

所以我们需要用本篇的主角—— AtomicInteger 来保证线程安全。

AtomicInteger 的源码如下:

package java.util.concurrent.atomic;
import sun.misc.Unsafe;

public class AtomicInteger extends Number implements java.io.Serializable {
    private static final long serialVersionUID = 6214790243416807050L;

    // setup to use Unsafe.compareAndSwapInt for updates
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final long valueOffset;

    static {
      try {
        valueOffset = unsafe.objectFieldOffset
            (AtomicInteger.class.getDeclaredField("value"));
      } catch (Exception ex) { throw new Error(ex); }
    }

    private volatile int value;

    public AtomicInteger(int initialValue) {
        value = initialValue;
    }

    public AtomicInteger() {
    }

    public final int get() {
        return value;
    }

    public final void set(int newValue) {
        value = newValue;
    }

    public final void lazySet(int newValue) {
        unsafe.putOrderedInt(this, valueOffset, newValue);
    }

    public final int getAndSet(int newValue) {
        for (;;) {
            int current = get();
            if (compareAndSet(current, newValue))
                return current;
        }
    }

    public final boolean compareAndSet(int expect, int update) {
    return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }

    public final boolean weakCompareAndSet(int expect, int update) {
    return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }

    public final int getAndIncrement() {
        for (;;) {
            int current = get();
            int next = current + 1;
            if (compareAndSet(current, next))
                return current;
        }
    }

    public final int getAndDecrement() {
        for (;;) {
            int current = get();
            int next = current - 1;
            if (compareAndSet(current, next))
                return current;
        }
    }

    public final int getAndAdd(int delta) {
        for (;;) {
            int current = get();
            int next = current + delta;
            if (compareAndSet(current, next))
                return current;
        }
    }

    public final int incrementAndGet() {
        for (;;) {
            int current = get();
            int next = current + 1;
            if (compareAndSet(current, next))
                return next;
        }
    }

    public final int decrementAndGet() {
        for (;;) {
            int current = get();
            int next = current - 1;
            if (compareAndSet(current, next))
                return next;
        }
    }
  
    public final int addAndGet(int delta) {
        for (;;) {
            int current = get();
            int next = current + delta;
            if (compareAndSet(current, next))
                return next;
        }
    }

    public String toString() {
        return Integer.toString(get());
    }


    public int intValue() {
    return get();
    }

    public long longValue() {
    return (long)get();
    }

    public float floatValue() {
    return (float)get();
    }

    public double doubleValue() {
    return (double)get();
    }

}

我们先看原子整型类中定义的属性:

// setup to use Unsafe.compareAndSwapInt for updates
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final long valueOffset;

    static {
      try {
        valueOffset = unsafe.objectFieldOffset
            (AtomicInteger.class.getDeclaredField("value"));
      } catch (Exception ex) { throw new Error(ex); }
    }

Unsafe是JDK内部的工具类,主要实现了平台相关的操作。下面内容引自

sun.misc.Unsafe是JDK内部用的工具类。它通过暴露一些Java意义上说“不安全”的功能给Java层代码,来让JDK能够更多的使用Java代码来实现一些原本是平台相关的、需要使用native语言(例如C或C++)才可以实现的功能。该类不应该在JDK核心类库之外使用。

Unsafe的具体实现跟本篇的目标关联不大,你只要知道这段代码是为了获取value在堆内存中的偏移量就够了。偏移量在AtomicInteger中很重要,原子操作都靠它来实现。

二、value和volatile

由源码可以看出:

private volatile int value;

value是由volatile来修饰,那么volatile的作用是什么呢?

volatile相当于synchronized的弱实现,也就是说volatile实现了类似synchronized的语义,却又没有锁机制。它确保对volatile字段的更新以可预见的方式告知其他的线程。也就是说当一个线程修改了共享变量之后,其他线程能够获取到共享变量修改后的值。

三、用CAS来实现安全的自增

AtomicInteger中有很多方法,例如inscrementAndGet() 相当于i++ 和getAndAdd() 相当于i+=n 。从源码中我们可以看出这几种方法的实现很相似,所以我们主要分析inscrementAndGet() 方法的源码。

public final int incrementAndGet() {
        for (;;) {
            int current = get();
            int next = current + 1;
            if (compareAndSet(current, next))
                return next;
        }
    }

 public final boolean compareAndSet(int expect, int update) {
    return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
 }

inscrementAndGet() 方法实现了自增的操作。核心实现是先获取当前值和目标值(也就是value+1),如果compareAndSet(current,next) 返回成功则该方法返回目标值。那么compareAndSet是做什么的呢?理解这个方法我们需要引入CAS操作。

在大学操作系统课程中我们学过独占锁和乐观锁的概念。独占锁就是线程获取锁后其他的线程都需要挂起,直到持有独占锁的线程释放锁;乐观锁是先假定没有冲突直接进行操作,如果因为有冲突而失败就重试,直到操作成功。其中乐观锁用到的机制就是CAS,Compare and Swap。

AtomicInteger 中的CAS操作就是compareAndSet(),其作用是每次从内存中根据内存偏移量(valueOffSet)取出数据,将取出的值跟expect 比较,如果数据一致就把内存中的值改为update。

这样使用CAS就保证了原子操作。其余几个方法的原理跟这个相同,在此不再过多的解释。

没看AtomicInteger 源码之前,我认为其内部是用synchronized来实现的原子操作。查阅资料后发现synchronized会影响性能,因为Java中的synchronized锁是独占锁,虽然可以实现原子操作,但是这种实现方式的并发性能很差。

四、总结

当我们需要计数器的时候可以用AtomicInteger类来实现,这样可以并发编程中保证线程安全。其内部主要依靠JDK 中的unsafe 类操作内存中的数据来实现的。volatile 修饰符保证了value在内存中其他线程可以看到其值得改变。CAS操作保证了AtomicInteger 可以安全的修改value 的值。

转载于:https://my.oschina.net/chenzhou/blog/2906939

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值