你的位置:首页 > Java教程

[Java教程]【JAVA 并发】atomic包


参考资料:http://ifeve.com/java-atomic/

atomic包的作用

方便程序员在多线程环境下,无锁的进行原子操作

atomic包核心

Atomic包里的类基本都是使用Unsafe实现的包装类,核心操作是CAS原子操作;

CAS

compare and swap,比较和替换技术,将预期值与当前变量的值比较(compare),如果相等则使用新值替换(swap)当前变量,否则不作操作;

现代CPU已广泛支持CAS指令;

另外,有一点需要注意的是CAS操作中的ABA问题,即将预期值与当前变量的值比较的时候,即使相等也不能保证变量没有被修改过,因为变量可能由A变成B再变回A,解决该问题,可以给变量增加一个版本号,每次修改变量时版本号自增,比较的时候,同时比较变量的值和版本号即可;

atomic包主要提供四种原子更新方式

  • 原子方式更新基本类型;
  • 原子方式更新数组;
  • 原子方式更新引用;
  • 原子方式更新字段;

原子方式更新基本类型

以下三个类是以原子方式更新基本类型

  • AtomicBoolean:原子更新布尔类型。
  • AtomicInteger:原子更新整型。
  • AtomicLong:原子更新长整型。

以AtomicInteger为例,

package concurrency;import java.util.concurrent.atomic.AtomicInteger;public class AtomicIntegerTest {  static AtomicInteger ai = new AtomicInteger(1);  public static void main(String[] args) {    //相当于i++,返回的是旧值,看方法名就知道,先获取再自增    System.out.println(ai.getAndIncrement());    System.out.println(ai.get());    //先自增,再获取    System.out.println(ai.incrementAndGet());    System.out.println(ai.get());    //增加一个指定值,先add,再get    System.out.println(ai.addAndGet(5));    System.out.println(ai.get());    //增加一个指定值,先get,再set    System.out.println(ai.getAndSet(5));    System.out.println(ai.get());  }}

注意:Atomic包提供了三种基本类型的原子更新,剩余的Java的基本类型还有char,float和double等,其更新方式可以参考AtomicBoolean的思路来现,AtomicBoolean是把boolean转成整型再调用compareAndSwapInt进行CAS来实现的;

原子方式更新数组

以下三个类是以原子方式更新数组,

  • AtomicIntegerArray:原子更新整型数组里的元素。
  • AtomicLongArray:原子更新长整型数组里的元素。
  • AtomicReferenceArray:原子更新引用类型数组里的元素。

以AtomicIntegerArray为例,其方法与AtomicInteger很像,多了个数组下标索引;

package concurrency;import java.util.concurrent.atomic.AtomicIntegerArray;public class AtomicIntegerArrayTest {  static int[] valueArr = new int[] { 1, 2 };  //AtomicIntegerArray内部会拷贝一份数组  static AtomicIntegerArray ai = new AtomicIntegerArray(valueArr);  public static void main(String[] args) {    ai.getAndSet(0, 3);    //不会修改原始数组value    System.out.println(ai.get(0));    System.out.println(valueArr[0]);  }}

原子方式更新引用

以下三个类是以原子方式更新引用,与其它不同的是,更新引用可以更新多个变量,而不是一个变量;

  • AtomicReference:原子更新引用类型。
  • AtomicReferenceFieldUpdater:原子更新引用类型里的字段。
  • AtomicMarkableReference:原子更新带有标记位的引用类型。

以AtomicReference为例,

package concurrency;import java.util.concurrent.atomic.AtomicReference;public class AtomicReferenceTest {  public static AtomicReference<User> atomicUserRef = new AtomicReference<User>();  public static void main(String[] args) {    User user = new User("conan", 15);    atomicUserRef.set(user);    User updateUser = new User("Shinichi", 17);    atomicUserRef.compareAndSet(user, updateUser);    System.out.println(atomicUserRef.get().getName());    System.out.println(atomicUserRef.get().getOld());  }  static class User {    private String name;    private int old;    public User(String name, int old) {      this.name = name;      this.old = old;    }    public String getName() {      return name;    }    public int getOld() {      return old;    }  }}

原子方式更新字段

以下三个类是以原子方式更新字段,

  • AtomicIntegerFieldUpdater:原子更新整型字段的更新器。
  • AtomicLongFieldUpdater:原子更新长整型字段的更新器。
  • AtomicStampedReference:原子更新带有版本号的引用类型,用于解决使用CAS进行原子更新时,可能出现的ABA问题。

以AtomicIntegerFieldUpdater为例,

package concurrency;import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;public class AtomicIntegerFieldUpdaterTest {  private static AtomicIntegerFieldUpdater<User> a = AtomicIntegerFieldUpdater      .newUpdater(User.class, "old");  public static void main(String[] args) {    User conan = new User("conan", 10);    System.out.println(a.getAndIncrement(conan));    System.out.println(a.get(conan));  }  public static class User {    private String name;    //注意需要用volatile修饰    public volatile int old;    public User(String name, int old) {      this.name = name;      this.old = old;    }    public String getName() {      return name;    }    public int getOld() {      return old;    }  }}