【Java多线程与并发库】07 Java中的13个原子操作类

【Java多线程与并发库】07 Java中的13个原子操作类

当程序更新一个变量时,如果多线程同时更新这个变量,可能得到期望之外的值,比如变量 i=1, A线程更新 i + 1, B线程也更新 i + 1,经过两个线程操作之后可能 i 不等于 3,而是等于 2。

因为 A 和 B 线程在更新变量 i 的时候获得的 i 都是 1,这就是线程不安全的更新操作,通常我们会使用synchronized来解决这个问题,synchronized会保证多线程不会同时更新变量 i。

而JDK1.5开始提供了java.util.concurrent.atomic包,这个包中的原子操作类提供了一种用法简单、性能高效、线程安全地更新一个变量的方式。

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

原子更新基本类型

下面我们学习一下原子更新基本类型类的使用。

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

  • AtomicInteger,原子更新整型变量
  • AtomicBoolean,原子更新布尔类型变量
  • AtomicLong,原子更新长整型变量

以上3个类提供的方法类似,这里我们主要学习AtomicInteger的基本使用。

int addAndGet(int delta)

boolean compareAndSet(int expect, int update)

int decrementAndGet()

double doubleValue()

float floatValue()

int get()

int getAndAccumulate(int x, IntBinaryOperator accumulatorFunction)

int getAndAdd(int delta)

int getAndDecrement()

int getAndIncrement()

int getAndSet(int newValue)

int incrementAndGet()

int intValue()

long longValue()

void set(int newValue)

由于方法命名恰到好处,我们可以非常容易地从方法名字知道每一个方法的用处。

下面给出使用部分方法的示例代码:

import java.util.concurrent.atomic.AtomicInteger;

/**
 * description:
 *
 * @author liyazhou
 * @since 2017-08-20 14:16
 */
public class AtomicIntegerTest {
    private static AtomicInteger ai = new AtomicInteger(1);

    public static void main(String... args){
        System.out.println("ai.getAndIncrement() = " + ai.getAndIncrement());
        System.out.println("ai.get() = " + ai.get());
        System.out.println("ai.getAndDecrement() = " + ai.getAndDecrement());
        System.out.println("ai.get() = " + ai.get());
        System.out.println("ai.getAndSet(10) = " + ai.getAndSet(10));
        System.out.println("ai.get() = " + ai.get());
    }
}

执行结果如下:

ai.getAndIncrement() = 1
ai.get() = 2
ai.getAndDecrement() = 2
ai.get() = 1
ai.getAndSet(10) = 1
ai.get() = 10

那么getAndIncrement方法是如何实现原子操作的呢?

其源码如下

 public final int getAndIncrement() {
     return 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;
}
public native int getIntVolatile(Object var1, long var2);
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);

个人的理解是:
重点看getAndAddInt方法
如果通过getIntVolatile方法获得的当前值和compareAndSwapInt获得当前值一样,则在compareAndSwapInt方法中更新var5为 var5+var4,此处 var4 = 1;否则,就循环执行这两个操作,直到更新成功为止。

原子更新数组

通过原子的方式更新数组里的某个元素,Atomic包提供了以下 4 个类。

AtomicIntegerArray
AtomicLongArray
AtomicReferenceArray
AtomicIntegerArray

原子更新引用类型

原子更新基本类型的AtomicInteger,只能更新一个变量,如果要原子更新多个变量,就需要使用原子更新引用类型提供的类。Atomic包提供了以下 3 个类。

AtomicReference
AtomicReferenceFieldUpdater
AtomicMarkableReference

原子更新字段类

如果需要原子地更新某个类里的某个字段时,就需要使用原子更新字段类,Atomic包提供了以下 3 个类进行原子字段更新。

AtomicIntegerFieldUpdater
AtomicLongFieldUpdater
AtomicStampedReference


参考:

《Java并发编程的艺术》

你可能感兴趣的:(Java,并发编程/网络编程/Netty)