您的位置:首页 > 编程语言 > Java开发

Java并发编程之原子变量

2017-10-22 13:33 323 查看
原子变量最主要的一个特点就是所有的操作都是原子的,synchronized关键字也可以做到对变量的原子操作。只是synchronized的成本相对较高,需要获取锁对象,释放锁对象,如果不能获取到锁,还需要阻塞在阻塞队列上进行等待。而如果单单只是为了解决对变量的原子操作,建议使用原子变量。关于原子变量的介绍,主要涉及以下内容:
原子变量的基本概念
通过AtomicInteger了解原子变量的基本使用
通过AtomicInteger了解原子变量的基本原理
AtomicReference的基本使用
使用FieldUpdater操作非原子变量的字段属性
经典的ABA问题的解决

一、原子变量的基本概念

     原子变量保证了该变量的所有操作都是原子的,不会因为多线程的同时访问而导致脏数据的读取问题。我们先看一段synchronized关键字保证变量原子性的代码:
public class Counter {
private int count;

public synchronized void addCount(){
this.count++;
}
}


简单的count++操作,线程对象首先需要获取到Counter 类实例的对象锁,然后完成自增操作,最后释放对象锁。整个过程中,无论是获取锁还是释放锁都是相当消耗成本的,一旦不能获取到锁,还需要阻塞当前线程等等。

对于这种情况,我们可以将count变量声明成原子变量,那么对于count的自增操作都可以以原子的方式进行,就不存在脏数据的读取了。Java给我们提供了以下几种原子类型:
AtomicInteger和AtomicIntegerArray:基于Integer类型
AtomicBoolean:基于Boolean类型
AtomicLong和AtomicLongArray:基于Long类型
AtomicReference和AtomicReferenceArray:基于引用类型

在本文的余下内容中,我们将主要介绍AtomicInteger和AtomicReference两种类型,AtomicBoolean和AtomicLong的使用和内部实现原理几乎和AtomicInteger一样。

二、AtomicInteger的基本使用

     首先看它的两个构造函数:
private volatile int value;

public AtomicInteger(int initialValue) {
value = initialValue;
}
public AtomicInteger() {

}


可以看到,我们在通过构造函数构造AtomicInteger原子变量的时候,如果指定一个int的参数,那么该原子变量的值就会被赋值,否则就是默认的数值0。

也有获取和设置这个value值的方法:
public final int get()
public final void set(int newValue)


当然,这两个方法并不是原子的,所以一般也很少使用,而以下的这些基于原子操作的方法则相对使用的频繁,至于它们的具体实现是怎样的,我们将在本文的后续小节中进行简单的学习。
//基于原子操作,获取当前原子变量中的值并为其设置新值
public final int getAndSet(int newValue)
//基于原子操作,比较当前的value是否等于expect,如果是设置为update并返回true,否则返回false
public final boolean compareAndSet(int expect, int update)
//基于原子操作,获取当前的value值并自增一
public final int getAndIncrement()
//基于原子操作,获取当前的value值并自减一
public final int getAndDecrement()
//基于原子操作,获取当前的value值并为value加上delta
public final int getAndAdd(int delta)
//还有一些反向的方法,比如:先自增在获取值的等等


下面我们实现一个计数器的例子,之前我们使用synchronized实现过,现在我们使用原子变量再次实现该问题。
//自定义一个线程类
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: