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

在多线程场景下,为了保证操作对象的安全性,通常使用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.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();

	// 将当前值+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#

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值