当程序更新一个变量时,如果多线程同时更新这个变量,可能得到期望之外的值,比如变量i = 1;A线程更新i + 1,B线程也更新i + 1,经过两个线程操作之后可能i不等于3,而是等于2,。因为A和B线程在更新变量i的时候拿到的i都是1,这就是线程不安全的更新操作,通常我们会使用synchronized来解决这个问题,synchronized会保证多线程不会同时更新变量i.
而Java从JDK1.5开始提供了java.util.concurrent.atomic(以下简称Atomic包),这个包中的原子操作类提供了一种用法简单、性能高效、线程安全地更新一个变量的方式。
因为变量的类型有很多种,所以在Atomic包里一共提供了13个类,属于4种类型的原子更新方式,分别是原子更新基本类型、原子更新数组、原子更新引用和原子更新属性(字段)。Atomic包里的类基本都是使用Unsafe实现的包装类。
原子更新基本类型类
使用原子的方式更新基本类型,Atomic包提供了以下3个类:
- AtomicBoolean:原子更新布尔类型
- AtomicInteger:原子更新整型
- AtomicLong:原子更新长整型
以上3个类提供的方法几乎一模一样,所以本节仅以AtomicInteger为例进行详解,AtomicInteger的常用方法如下:
(1)int addAndGet(int delta):以原子方式将输入的数值与实例中的值(AtomicInteger里的value)相加,并返回结果
(2)boolean compareAndSet(int expect, int update):如果输入的数值等于预期值,则以原子方式将该值设置为输入的值
(3)int getAndIncrement():以原子方式将当前值加1,注意,这里返回的是自增前的值
(4)void lazySet(int newValue):最终会设置成newValue,使用lazySet设置值后,可能导致其他线程在之后的一小段时间内还是可以读到旧的值
(5)int getAndSet(int newValue):以原子方式设置为newValue的值,并返回旧值
AtomicInteger示例代码如下
public class AtomicIntegerTest { static AtomicInteger atomicInteger = new AtomicInteger(1); public static void main(String[] args) { System.out.println(atomicInteger.getAndIncrement()); System.out.println(atomicInteger.get()); } }
输出1 2
那么getAndIncrement()是如何实现原子操作的呢?让我们一起分析其实现原理
public final int getAndIncrement(){ for(;;){ int current = get(); int next = current + 1; if(compareAndSet(current, next)) return current; } } public final boolean compareAndSet(int expect, int update){ return unsafe.compareAndSwapInt(this, valueOffset, expect, update); }
源码中for循环体的第一步先取得AtomicInteger里存储的数值,第二步对AtomicInteger的当前数值进行加1操作,关键的第三步调用compareAndSet方法来进行原子更新操作,该方法先检查当前数值是否等于current,等于意味着AtomicInteger的值没有被其他线程修改过,则将AtomicInteger的当前值更新成next的值,如果不等compareAndSet方法会返回false,程序会进入for循环重新进行compareAndSet操作。
Atomic包提供了3种基本类型的原子更新,但是Java的基本类型里还有char、float和double等。
那么如何原子的更新其他的基本类型呢?
Atomic包里的类基本都是使用Unsafe实现的,我们发现Unsafe只提供了3种CAS方法:compareAndSwapObject、compareAndSwapInt和compareAndSwapLong,再看AtomicBoolean源码,发现它是先把Boolean转换成整型,再使用compareAndSwapInt进行CAS,所以原子更新char、float和double变量也可以用类似的思路来实现。
原子更新数组
通过原子的方式更新数组里的某个元素,Atomic包提供了以下4个类:
- AtomicIntegerArray:原子更新整型数组里的元素
- AtomicLongArray:
原文地址:https://www.cnblogs.com/hzzjj/p/9738076.html