并发基础_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.Unsafe是JDK提供乐观锁的支持。
所以在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#
- 并发基础_14_并发_原子操作类
- java并发中的原子操作类
- 并发编程之原子操作
- java原子操作与并发
- Java 原子操作与并发
- Java并发编程系列之十九:原子操作类
- 【java并发】原子性操作类的使用
- java多线程之线程并发库原子性操作类
- Java并发编程(七)《12个原子操作类》
- java并发基础(九)--- 原子变量
- linux并发控制之原子操作
- linux并发控制之原子操作
- 设备驱动-----并发控制--原子操作
- linux并发控制之原子操作
- 多线程并发锁和原子操作
- java 递增不是原子操作-并发
- MongoDB upsert操作并发的原子性
- 并发编程—OSAtomic原子操作
- PLSQL学习笔记(二)
- 9.14
- eclispe快捷键
- [leetcode-13] Roman to Integer
- c++中stl的map的[]取下标运算符需要慎用
- 并发基础_14_并发_原子操作类
- 软件框架之EventBus的使用
- BusyBox制作Initramfs嵌入式Linux根文件系统
- void用法
- 两个结构体ifconf和ifreq
- 客户端WebP 图片格式优化
- fedora安装后的配置
- 3.复杂度分析
- 内部类的基本使用(用匿名内部类排序数组)