1、概述 java
Java从JDK1.5开始提供了java.util.concurrent.atomic包,方便程序员在多线程环境下无锁的进行原子操做。原子变量的底层使用了处理器提供的原子指令,可是不一样的CPU架构可能提供的原子指令不同,也有可能须要某种形式的内部锁,因此该方法不能绝对保证线程不被阻塞。程序员
atomic包里的类基本都是使用Unsafe实现的包装类,在Atomic包里一共有12个类,四种原子更新方式,这里咱们将对着四种方式进一步分析。算法
2、解析
1. 原子更新基本类型类
用于经过原子的方式更新基本类型,Atomic包提供了如下三个类:
AtomicBoolean:原子更新布尔类型。
AtomicInteger:原子更新整型。
AtomicLong:原子更新长整型。数组
这里咱们着重介绍下AtomicInteger这个类的经常使用方法,以下:
(1)int addAndGet(int delta) :安全
以原子方式将输入的数值与实例中的值(AtomicInteger里的value)相加,并返回结果
(2)boolean compareAndSet(int expect, int update) :多线程
若是输入的数值等于预期值,则以原子方式将该值设置为输入的值。
(3)int getAndIncrement():架构
以原子方式将当前值加1,注意:这里返回的是自增前的值。
(4)void lazySet(int newValue)
(5)int getAndSet(int newValue):性能
以原子方式设置为newValue的值,并返回旧值。this
参考实例以下:atom
AtomicInteger a = new AtomicInteger(1); a.addAndGet(2); System.out.println(a); //3 a.getAndIncrement(); System.out.println(a.get()); //4
这里咱们延伸一下,介绍下CAS的实现原理:
CAS(Compare and Swap,比较并操做), CAS是项乐观锁技术,当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是被告知此次竞争中失败,并能够再次尝试。
CAS有3个操做数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改成B,不然什么都不作。
AtomicInteger中的 incrementAndGet 方法源码以下:
public final int getAndIncrement() { for (;;) { int current = get(); int next = current + 1; if (compareAndSet(current, next)) return current; } }
经过源码能够看到,方法执行步骤为:先获取到当前的 value 属性值,而后将 value 加 1,赋值给一个局部的 next 变量,然而,这两步都是非线程安全的,可是内部有一个死循环,不断去作compareAndSet操做,直到成功为止,也就是修改的根本在compareAndSet方法里面,compareAndSet()方法的代码以下:
public final boolean compareAndSet(int expect, int update) { return unsafe.compareAndSwapInt(this, valueOffset, expect, update); }
compareAndSwapInt 方法是一个native 方法,以下:
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
因此compareAndSet传入的值为执行方法时获取到的value属性值,next为加 1 后的值,compareAndSet调用native的compareAndSwapInt方法来完成,
compareAndSwapInt 基于的是CPU的CAS指令来实现的。因此基于CAS的操做可认为是无阻塞的,一个线程的失败或挂起不会引发其它线程也失败或挂起。而且因为CAS操做是CPU原语,因此性能比较好。
可是须要注意的是:在CAS操做中,会出现ABA的问题,即若是V值先由A变为B,再由B变为A,那么会仍然认为是发生了变化,并须要从新执行算法中的步骤。
对应的解决方案是:不仅更新某个引用的值,而是更新两个值,包括一个引用和一个版本号,即便这个值由A变为B,而后又变为A,版本号也是不一样的。
例如:AtomicMarkableReference和AtomicStampedReference支持在两个变量上执行原子的条件更新,AtomicStampedReference更新一个“对象-引用”二元组,经过在引用上加上“版本号”,从而避免ABA问题,AtomicMarkableReference将更新一个“对象引用-布尔值”的二元组。
2. 原子更新数组类
经过原子的方式更新数组里的某个元素,Atomic包提供了如下三个类:
AtomicIntegerArray:原子更新整型数组里的元素,主要用于原子的方式更新数组里的整型。
AtomicLongArray:原子更新长整型数组里的元素。
AtomicReferenceArray:原子更新引用类型数组里的元素。
这里咱们一样着重介绍下AtomicIntegerArray类的经常使用方法,以下:
(1)int addAndGet(int i, int delta):
以原子方式将输入值与数组中索引i的元素相加。
(2)boolean compareAndSet(int i, int expect, int update):
若是当前值等于预期值,则以原子方式将数组位置i的元素设置成update值。
参考实例以下:
int[] arr = new int[]{1,2,3};
AtomicIntegerArray arr1= new AtomicIntegerArray(arr);
arr1.getAndAdd(2,4);
arr1.getAndSet(0,2);
System.out.println(arr[2]); //3
System.out.println(arr1.get(2)); //7
注:AtomicIntegerArray对内部的数组元素进行修改时,不会影响到传入的数组,缘由是数组值经过构造方法传递进去,而后AtomicIntegerArray会将当前数组复制一份。
3. 原子更新引用类型
原子更新基本类型的AtomicInteger,只能更新一个变量,若是要原子的更新多个变量,就须要使用这个原子更新引用类型提供的类。Atomic包提供了如下三个类:
AtomicReference:原子更新引用类型。
AtomicReferenceFieldUpdater:原子更新引用类型里的字段。
AtomicMarkableReference:原子更新带有标记位的引用类型。能够原子的更新一个布尔类型的标记位和引用类型。构造方法是AtomicMarkableReference(V initialRef, boolean initialMark)
参考实例以下:
static class User{ String id; String name; volatile int age; public User(String id, String name, int age) { this.id = id; this.name = name; this.age = age; } public String getId() { return id; } public String getName() { return name; } public int getAge() { return age; } } public static void main(String[] args) { User user1 = new User("1", "LiLei", 11); User user2 = new User("2", "HanMeimei", 12); AtomicReference<User> user = new AtomicReference<User>(user1); System.out.println(user1.getName()); //LiLei user.getAndSet(user2); System.out.println(user.get().getName()); //HanMeimei }
4. 原子更新字段类
若是咱们只须要某个类里的某个字段,那么就须要使用原子更新字段类,Atomic包提供了如下三个类:
AtomicIntegerFieldUpdater:原子更新整型的字段的更新器。
AtomicLongFieldUpdater:原子更新长整型字段的更新器。
AtomicStampedReference:原子更新带有版本号的引用类型。该类将整数值与引用关联起来,可用于原子的更数据和数据的版本号,能够解决使用CAS进行原子更新时,可能出现的ABA问题。
原子更新字段类都是抽象类,每次使用都时候必须使用静态方法newUpdater建立一个更新器。原子更新类的字段的必须使用public volatile修饰符。
AtomicIntegerFieldUpdater的例子代码以下:
AtomicIntegerFieldUpdater<User> userUpdate = AtomicIntegerFieldUpdater.newUpdater(User.class, "age"); userUpdate.getAndIncrement(user2); System.out.println(user2.getAge()); //13
参考博文:
http://ifeve.com/java-atomic/ http://blog.csdn.net/qfycc92/article/details/46489553