在多线程场景下,为了保证操作对象的安全性,通常使用synchronized来解决。
在JDK1.5开始,提供了java.util.concurrent.atomic包,这个包中的原子操作类提供了一些简单安全高效的更新变量的方式。
Atomic包中一共提供了13个类,Atomic包中的类基本都是使用Unsafe实现的包装类。
所谓原子操作:
一组不可分割的操作;
操作者对目标对象进行操作时,要么完成所有操作后其他操作者才能操作;
要么这个操作者不能进行任何操作。
原子操作基本类型
AtomicBoolean:布尔类型的原子操作
AtomicInteger:整型数字的原子操作
AtomicLong:长整型数字的原子操作
我们来看一个AtomicInteger的Demo:
public static void main(String[] args) {
// 实例化一个AtomicInteger类的对象atomic,并定义其初始值为1
AtomicInteger 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 updates
private 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();
// 将当前值+1
int 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();
// 将当前值+1
int next = current + 1;
// 这里是关键,使用JDK中的CAS机制
// 对当前值和预期值进行比较
// 如果当前值与预期值不一致,说明有某一个其他线程完成了值的更改
// 那么进行下一次循环,进行重新计算(因为之前的计算结果不对了)
if (compareAndSet(current, next))
return next;
}
}
这就是整个利用乐观锁进行原子操作的过程。
原子操作数组
AtomicIntegerArray:原子操作整型数组
AtomicLongArray:原子操作长整型数组
AtomicReferenceArray:原子操作对象引用数组
我们来看一个AtomicIntegerArray操作的Demo:
@Test
public void testAtomicIntegerArray() {
// 创建给定长度的新 AtomicIntegerArray
AtomicIntegerArray 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;
}
}
@Test
public 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;
}
}
@Test
public 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@4224753d
newPerformance : com.atomuic.atomic_1.Performance@678d19b7 , otherPerformance : com.atomuic.atomic_1.Performance@6093727d
从输出结果可以看出,student对象是同一个,performance属性被修改了..
达到了目的
参考资料:
http://blog.csdn.net/yinwenjie/article/details/50698751#