Java原子更新基本类型

当程序更新一个变量时,如果是多线程同时更新这个变量,可能得到期望之外的值,比如变量i=1,A线程更新i+1,B线程也更新i+1,经过两个线程操作之后可能i不等于3,而是等于2。因为A、B线程在更新i时拿到的i都是1,这就是线程的不安全更新操作,通常我们会使用Synchronized来解决这个问题(也可以通过加锁的方式 ),synchronized(或加锁)会保证多线程不会同时更新变量i。

而在JDK的java.util.concurrent.atomic包中的原子类提供了一种用法简单、性能高效、线程安全地更新一个变量的方式。

因为类型有很多种,所以在Atomic包中提供了13个类,4种类型的原子更新方式,分别是原子更新基本类型,原子更新数组,原子更新引用和原子更新属性(字段)。Atomic中的类基本都是使用Unsafe实现的包装类

原子更新基本类型

使用原子的方式更新基本类型,Atomic包提供了以下三个类。

AtomicBoolean:原子更新布尔类型。

AtomicInteger原子更新整型。

AtomicLong原子更新长整型。

这三个类提供的方法基本一样,下面以AtomicInteger来讲解,AtomicInteger的常用方法如下。

getAndAdd


/**
     * Atomically adds the given value to the current value.
     *
     * @param delta the value to add
     * @return the previous value
     */
    public final int getAndAdd(int delta) {
        return unsafe.getAndAddInt(this, valueOffset, delta);
    }

 

compareAndSet

   
/**
     * Atomically sets the value to the given updated value
     * if the current value {@code ==} the expected value.
     *
     * @param expect the expected value
     * @param update the new value
     * @return {@code true} if successful. False return indicates that
     * the actual value was not equal to the expected value.
     */
    public final boolean compareAndSet(int expect, int update) {
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }

 

getAndIncrement 以原子方式将当前值加1,注意,这里返回的是自增前的值

  
/**
     * Atomically increments by one the current value.
     *
     * @return the previous value
     */
    public final int getAndIncrement() {
        return unsafe.getAndAddInt(this, valueOffset, 1);
    }

 

getAndSet 返回的是旧值(细心留意方法名可以发现,get在前面是获取旧值再操作,Get在后面是获取操作后的新值)

 
/**
     * 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) {
        return unsafe.getAndSetInt(this, valueOffset, newValue);
    }

 

示例


/**
 * @author perist
 * @date 2017/3/19
 * @time 22:56
 */
public class AtomicIntegerTest {
static AtomicInteger atomicInteger = new AtomicInteger(1);

public static void main(String[] args) {

    System.out.println(atomicInteger.getAndIncrement());
    System.out.println(atomicInteger.get());
}

}

该示例输出


1
2

Process finished with exit code 0

 

那么getAndIncrement 是如何实现原子操作的呢,?我们分析下其源码:

unsafe.getAndAddInt(this, valueOffset, 1);
 
public final int getAndAddInt(Object var1, long var2, int var4) {
        int var5;
        do {
            var5 = this.getIntVolatile(var1, var2);
        } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
    return var5;
}

关键是调用compareAndSwapInt,该方法先检查当前数值是否等于var5,等于意味着AtomicInteger没有被其他线程修改过,则将AtomicInteger的当前值更新为var5+var4,如果不等于compareAndSwapInt返回false,程序会进入do...while重新进入操作

Atomic包提供了3种基本类型的原子更新,但是Java的基本类型还有char,float,double等。那么问题来了如何更新其他的基本类型呢,?atomic包里的类基本都是Unsafe实现的,让我们看一下Unsafe源码:


public final native boolean compareAndSwapInt(Object o,
 long offset,
 int expected, 
 int x);
public final native boolean compareAndSwapLong(Object o,
 long offset,
 long expected,
 long x);
public final native boolean compareAndSwapObject(Object o,
 long offset,
 Object expected,
 Object x);

 

通过源码,我们发现Unsafe只提供了3种CAS操作,再看AtomicBoolean源码,发现它也是先把Boolean转换成整型,再使用compareAndSwapInt进行CAS操作的,所以原子更新char,float,double也可以用类似的思路来解决。

 

 

0 0 投票数
Article Rating
订阅评论
提醒
guest
0 评论
最旧
最新 最多投票
内联反馈
查看所有评论
0
希望看到您的想法,请您发表评论x