并发基础_14_并发_原子操作类

来源:互联网 发布:文学创作知乎 编辑:程序博客网 时间:2024/06/05 06:04

在多线程场景下,为了保证操作对象的安全性,通常使用synchronized来解决。

在JDK1.5开始,提供了java.util.concurrent.atomic包,这个包中的原子操作类提供了一些简单安全高效的更新变量的方式。

Atomic包中一共提供了13个类,Atomic包中的类基本都是使用Unsafe实现的包装类。


所谓原子操作:

一组不可分割的操作;

操作者对目标对象进行操作时,要么完成所有操作后其他操作者才能操作;

要么这个操作者不能进行任何操作。


原子操作基本类型

AtomicBoolean:布尔类型的原子操作

AtomicInteger:整型数字的原子操作

AtomicLong:长整型数字的原子操作


我们来看一个AtomicInteger的Demo:

public static void main(String[] args) {// 实例化一个AtomicInteger类的对象atomic,并定义其初始值为1AtomicInteger atomicInteger = new AtomicInteger(1);// 进行atomic的原子化操作:增加1,并获取这个增加后的新值System.out.println(atomicInteger.incrementAndGet());}输出结果:2

通过上面简单的代码,我们看到了原子类的基本使用,这里可能会有疑问:这个和index++有什么区别吗?

这个和index++最大的区别就是:index++不是线程安全的。

为什么index++不是线程安全的,这里不赘述,点此链接:http://www.importnew.com/17056.html



原子操作的实现

查看AtomicInteger的源码,你会发现,源码中并没有synchronized,那么它是怎么实现线程安全的呢?

而且incrementAndGet方法中还有一个死循环...

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 updatesprivate 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;/** * Creates a new AtomicInteger with the given initial value. * * @param initialValue *            the initial value */public AtomicInteger(int initialValue) {value = initialValue;}/** * Creates a new AtomicInteger with initial value {@code 0}. */public AtomicInteger() {}/** * Gets the current value. * * @return the current value */public final int get() {return value;}/** * Sets to the given value. * * @param newValue *            the new value */public final void set(int newValue) {value = newValue;}/** * Eventually sets to the given value. * * @param newValue *            the new value * @since 1.6 */public final void lazySet(int newValue) {unsafe.putOrderedInt(this, valueOffset, newValue);}/** * 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) {for (;;) {int current = get();if (compareAndSet(current, newValue))return current;}}/** * 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 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);}/** * Atomically sets the value to the given updated value if the current value * {@code ==} the expected value. * * <p> * May <a href="package-summary.html#Spurious">fail spuriously</a> and does * not provide ordering guarantees, so is only rarely an appropriate * alternative to {@code compareAndSet}. * * @param expect *            the expected value * @param update *            the new value * @return true if successful. */public final boolean weakCompareAndSet(int expect, int update) {return unsafe.compareAndSwapInt(this, valueOffset, expect, update);}/** * Atomically increments by one the current value. * * @return the previous value */public final int getAndIncrement() {for (;;) {int current = get();int next = current + 1;if (compareAndSet(current, next))return current;}}/** * Atomically decrements by one the current value. * * @return the previous value */public final int getAndDecrement() {for (;;) {int current = get();int next = current - 1;if (compareAndSet(current, next))return current;}}/** * 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) {for (;;) {int current = get();int next = current + delta;if (compareAndSet(current, next))return current;}}/** * Atomically increments by one the current value. * * @return the updated value */public final int incrementAndGet() {// 一直循环的目的是为了"预期值"与"真实值"不一致的情况下,能够重新进行+1计算for (;;) {// 取得/重新取得当前的value值int current = get();// 将当前值+1int next = current + 1;// 这里是关键,使用JDK中的CAS机制// 对当前值和预期值进行比较// 如果当前值与预期值不一致,说明有某一个其他线程完成了值的更改// 那么进行下一次循环,进行重新计算(因为之前的计算结果不对了)if (compareAndSet(current, next))return next;}}/** * Atomically decrements by one the current value. * * @return the updated value */public final int decrementAndGet() {for (;;) {int current = get();int next = current - 1;if (compareAndSet(current, next))return next;}}/** * Atomically adds the given value to the current value. * * @param delta *            the value to add * @return the updated value */public final int addAndGet(int delta) {for (;;) {int current = get();int next = current + delta;if (compareAndSet(current, next))return next;}}/** * Returns the String representation of the current value. *  * @return the String representation of the current value. */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();}}

我们先来了解下乐观锁和悲观锁。

悲观锁是一种独占锁,它假设的前提是"冲突一定会发生",所以处理某段可以能出现数据冲突的代码时,这段代码就要被某个线程独占。

独占则意味着"其他即将执行这段代码的其他线程"都将进入"阻塞/挂起"状态

 

synchronized关键字就是Java对于悲观锁的实现。

 

有悲观锁的存在,就有乐观锁的存在。

乐观锁假定"冲突不一定会出现",如果出现冲突则进行重试,直到冲突消失。

由于乐观锁的假定条件,所以乐观锁不会独占资源,自然性能就会好于悲观锁

AtomicInteger是一个标准的乐观锁实现。sun.misc.UnsafeJDK提供乐观锁的支持。

所以在incrementAndGet方法中,会看到一个"死循环",这是incrementAndGet方法中有"比较--重试"的需求。

我们把incrementAndGet方法源码单独拎出来看看

/*** Atomically increments by one the current value.** @return the updated value*/public final int incrementAndGet() {// 一直循环的目的是为了"预期值"与"真实值"不一致的情况下,能够重新进行+1计算for (;;) {// 取得/重新取得当前的value值int current = get();// 将当前值+1int next = current + 1;// 这里是关键,使用JDK中的CAS机制// 对当前值和预期值进行比较// 如果当前值与预期值不一致,说明有某一个其他线程完成了值的更改// 那么进行下一次循环,进行重新计算(因为之前的计算结果不对了)if (compareAndSet(current, next))return next;}}
这就是整个利用乐观锁进行原子操作的过程。



原子操作数组

AtomicIntegerArray:原子操作整型数组

AtomicLongArray:原子操作长整型数组

AtomicReferenceArray:原子操作对象引用数组

我们来看一个AtomicIntegerArray操作的Demo:

@Testpublic void testAtomicIntegerArray() {// 创建给定长度的新 AtomicIntegerArrayAtomicIntegerArray intergerArr = new AtomicIntegerArray(5);// 设置指定索引位的数值// set(int i, int newValue):将位置 i 的元素设置为给定值。// intergerArr.set(0, 5);// addAndGet(int i, int delta):以原子方式将给定值与索引 i 的元素相加。intergerArr.addAndGet(0, 5);int current = intergerArr.decrementAndGet(0);System.out.println("current : " + current);}
其余方法请查阅 JDK API ....



原子操作对象字段

AtomicIntegerFieldUpdater:整型数据字段更新器

AtomicLongFieldUpdater:长整型数据字段更新器

AtomicReferenceFieldUpdater:对象数据字段更新器

AtomicReference:对象引用原子操作


java.util.concurrent.atomic还为我们提供了对对象进行原子操作的方式。

当然,这也是基于乐观锁的

我们来看一个AtomicReference的Demo:

/*** 代表学生成绩* * @author CYX* @time 2017年8月2日下午2:40:10*/public class Performance {public Integer getPerformance() {return performance;}public void setPerformance(Integer performance) {this.performance = performance;}private Integer performance;}public class Student {// 学生成绩private Performance performance;private String name;public Student(String name, Integer performance) {this.performance = new Performance();this.performance.setPerformance(performance);this.name = name;}public Performance getPerformance() {return performance;}public void setPerformance(Performance performance) {this.performance = performance;}public String getName() {return name;}public void setName(String name) {this.name = name;}}@Testpublic void testAtomicReference() {Student student = new Student("cyx", 80);AtomicReference<Student> ref = new AtomicReference<Student>(student);student = new Student("cyx", 70);Student oldStudent = ref.getAndSet(student);System.out.println(student + "和" + oldStudent + " 是两个对象");System.out.println("AtomicReference保证了赋值时的原子操作性");}输出结果:com.atomuic.atomic_1.Student@70419e13和com.atomuic.atomic_1.Student@63f12af8 是两个对象AtomicReference保证了赋值时的原子操作性
通过Demo,我们使用AtomicReference对某一个对象的赋值过程进行了操作。

但是很明显,最后出现了两个对象,这不是我们的目的,我们只是希望student对象不变,只改变student的成绩属性。

so,我们应当使用AtomicReferenceFieldUpdater


使用AtomicReferenceFieldUpdater构建Demo

/*** 代表学生成绩* * @author CYX* @time 2017年8月2日下午2:40:10*/public class Performance {public Integer getPerformance() {return performance;}public void setPerformance(Integer performance) {this.performance = performance;}private Integer performance;}public class Student {public Student() {}// 学生成绩private volatile Performance performance;// 学生成绩"更改者"private AtomicReferenceFieldUpdater<Student, Performance> updater = AtomicReferenceFieldUpdater.newUpdater(Student.class, Performance.class, "performance");private String name;public Student(String name, Integer performance) {this.performance = new Performance();this.performance.setPerformance(performance);this.name = name;}public Performance getPerformance() {return performance;}public void setPerformance(Performance performance) {updater.set(this, performance);}public String getName() {return name;}public void setName(String name) {this.name = name;}}@Testpublic void testAtomicReference() {Student student = new Student();Performance newPerformance = new Performance();newPerformance.setPerformance(80);// 注意,这样student中的performance属性,就是用了乐观机制,保证了操作的线程安全性。student.setPerformance(newPerformance);Performance otherPerformance = new Performance();otherPerformance.setPerformance(100);student.setPerformance(otherPerformance);System.out.println("new student : " + student + " , old student : " + student);System.out.println("newPerformance : " + newPerformance + " , otherPerformance : " + otherPerformance);}输出结果:new student : com.atomuic.atomic_1.Student@4224753d , old student : com.atomuic.atomic_1.Student@4224753dnewPerformance : com.atomuic.atomic_1.Performance@678d19b7 , otherPerformance : com.atomuic.atomic_1.Performance@6093727d

从输出结果可以看出,student对象是同一个,performance属性被修改了..

达到了目的


参考资料:
http://blog.csdn.net/yinwenjie/article/details/50698751#