您的位置 首页 java

Java并发包-atomic包-让您彻底掌握AtomicInteger源码

从这一篇文章开始,我开始对并发包的atomic包进行源码解析,首先通过下面的图了解以下atomic包中的类都是有哪些?

这一篇文章首先对AtomicInteger进行讲解。中我对并发包保证共享变量 线程安全 的机制做了讲解,如果对关键字volatile和CAS机制真正了解的话,AtomicInteger的内部原理是非常的容易理解的,那就跟着我的思路来打开AtomicInteger的大门吧。

本篇文章的主要内容:

1:AtomicInteger的实例
2:AtomicInteger的源码解析
 

1、AtomicInteger的实例

我首先利用一个简单的例子引入今天的话题:

public class AtomicIntegerTest {
 private static AtomicInteger ai = new AtomicInteger(0);
 private static int a = 0;
 public static void main(String[] args) throws InterruptedException {
 ordinaryIntegerTest();
 atomicIntegerTest();
 Thread.sleep(2000);
 System.out.println("a="+a);
 System.out.println("ai="+ai.get());
 }
 //普通变量多线程下累加
 public static void ordinaryIntegerTest(){
 for(int i = 0;i<10;i++){
 new Thread(()->{
 for(int j = 0;j<10000;j++){
 a++;
 }
 }).start();
 }
 }
//利用AtomicInteger多线程下累加
 public static void atomicIntegerTest(){
 for(int i = 0;i<10;i++){
 new Thread(()->{
 for(int j = 0;j<10000;j++){
 ai.incrementAndGet();
 }
 }).start();
 }
 }
}
 

通过上面的例子,大家能够猜出答案吗?运行结果如下:

通过上面两个运行结果可以看出:普通的变量每一次运行结果都不相同,而AtomicInteger变量每一次运行结果都是相同的,而且结果是正确的,所以在 多线程 下,普通变量无法保证线程安全,而AtomicInteger则在多线程下每一次结果都是正确的。那么我们知道了在多线程下AtomicInteger是线程安全的,它内部怎样实现的呢?我们接下来进入AtomicInteger源码一探究竟。

2、AtomicInteger的源码解析【Java自学网分析】

2.1、首先看一下AtomicInteger的定义

public class AtomicInteger extends  Number  implements java.io.Serializable {
 //代码省略
}
//Number类的定义:主要将当前类型转化为基本类型
public abstract class Number implements java.io.Serializable {
 //将当前类型转化成int
 public abstract int intValue();
 //将当前类型转化为long型
 public abstract long longValue();
 //将当前类型转化为float类型
 public abstract float floatValue();
 //将当前类型转化为double类型
 public abstract double doubleValue();
 //将当前类型转化为byte类型
 public byte byteValue() {
 return (byte)intValue();
 }
 //将当前类型转为short类型
 public short shortValue() {
 return (short)intValue();
 }
}
 

2.2、AtomicInteger的成员变量

我说过,在并发包中大部分都是利用关键字 volatile 和CAS机制进行保证并发安全的,所以大家应该可以才想到在AtomicInteger成员变量中必定会出现被关键字volatile修饰的变量和 原子 封装类Unsafe,接下来开始证明我们的猜想。

//unsafe:表示对 原子操作 的封装类
private static final Unsafe unsafe = Unsafe.getUnsafe();
//被volatile修饰的变量
private volatile long value;
 

看到上面的两个成员变量,大家是不是找到了一点感觉,原来AtomicInteger是通过关键字volatile和CAS保证多线程下安全的修改一个共享变量。接下来我们继续往下分析。

//值value在内存的偏移地址
private static final long valueOffset;
//静态代码块就是获取这个偏移地址的。
static {
 try {
 valueOffset = unsafe.objectFieldOffset
 (AtomicLong.class.getDeclaredField("value"));
 } catch (Exception ex) { throw new Error(ex); }
}
 

上面的valueOffset就是变量value在内存的地址偏移量,也就是上一篇我介绍的CAS(V,E,N)中的V。

在AtomicInteger中还有一个成员变量,如下:

//表示当前虚拟机是否支持long类型无锁化CAS机制
static final boolean VM_SUPPORTS_LONG_CAS = VMSupportsCS8();
private static native boolean VMSupportsCS8();
 

上面把AtomicInteger的成员变量都介绍完了,通过被volatile修饰的value和unsafe封装的原子操作通过无锁化来保证对一个变量的线程安全,我们并没有看到加锁,说明AtomicInteger是一种乐观锁。

2.3、AtomicInteger的 构造函数

//第一个构造函数:无参数构造
public AtomicLong() {
}
//第二个构造函数:传递一个初始化value值。
public AtomicLong(long initialValue) {
 value = initialValue;
}
 

构造函数非常的简单,我们接下来继续分析它的一些重要的方法。

2.4、AtomicInteger的重要方法

AtomicInteger类中主要分为四类方法,分别如下:

第一类:对单一value的操作。
第二类:对value的复合操作
第三类:对value更加复杂的操作。
第四类:对Number方法的实现。
 

第一类:对value的简单操作

//对volatile变量value的写
public final void set(long newValue) {
 value = newValue;
}
//对volatile变量value的读
public final long get() {
 return value;
}
 

上面两个方法是对voaltile变量的写和读,大家还记得volatile的两个特性吗?

1:任意对volatile变量的写,JMM都会立刻将工作内存中的值刷新到主存中。
2:任意对volatile变量的读,JMM都会从主存中复制一份到自己的工作内存。
 

上面的两个方法是对被volatile修饰的单一变量的写和读,并没有任何的复合操作,所以它具有 可见性 有序性 原子性 ,所以在多线程下是安全的。

第二类:对value的符合操作

上一篇文章我讲过关键字volatile只能保证可见性和有序性(禁止指令重排序),但是volatile不具备原子性的特点,所以对value的复合操作不能保证线程安全,所以需要在配合CAS原子操作才能保证多线程下对value修改的安全。

//这个方法就是我们所说的CAS
public final boolean compareAndSet(long expect, long update) {
 return unsafe.compareAndSwapLong(this, valueOffset, expect, update);
}
//通过CAS原子的设置value=newValue,然后返回旧值
public final int getAndSet(int newValue) {
 return unsafe.getAndSetInt(this, valueOffset, newValue);
}
//通过CAS原子的设置value+1,然后返回+1前的旧值
public final int getAndIncrement() {
 return unsafe.getAndAddInt(this, valueOffset, 1);
}
//通过CAS原子的设置value+delta,然后返回+delta前的旧值
public final int getAndAdd(int delta) {
 return unsafe.getAndAddInt(this, valueOffset, delta);
}
//通过CAS原子的设置value+1,然后返回+1后的新值
public final int incrementAndGet() {
 return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}
//通过CAS原子的设置value+delta,然后返回+delta后的新值
public final int addAndGet(int delta) {
 return unsafe.getAndAddInt(this, valueOffset, delta) + delta;
}
//通过CAS原子的设置value-1,然后返回-1前的旧值
public final int getAndDecrement() {
 return unsafe.getAndAddInt(this, valueOffset, -1);
}
//通过CAS原子的设置value-1,然后返回-1后的新值
public final int decrementAndGet() {
 return unsafe.getAndAddInt(this, valueOffset, -1) - 1;
}
 

上面的方法无非就是通过CAS操作+1或者-1,+delta或者-delta,要么返回旧值,要么返回新值,所以都是一些复合操作,上面的方法都是调用了Unsafe中的getAndInt()方法,那么我们继续跟进到Unsafe中看看这个方法。

public final int getAndAddInt(Object var1, long var2, int var4) {
 int var5;
 do {
 var5 = this.getIntVolatile(var1, var2);
 } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
 return var5;
}
 

上面这个方法是不是非常的熟悉,通过CAS操作,成功则返回,失败则继续自旋。流程图如下:

上面的几个方法是对value的复合操作,不像第一类操作,能够直接通过关键字volatile就可以保证线程安全,而由于volatile不具备原子性,所以对value的符合操作需要通过volatile+CAS保证线程安全。

1:对value的单一操作:直接通过volatile保证线程安全。
2:对value的复合操作:通过volatile+CAS保证线程安全。
 

第三类操作:通过传递功能接口实现对value更复杂的操作

那什么是对value更复杂的操作呢?第二类复合操作只是在原来value的基础上加上一个值或者减去一个值,那么我们向在value的基础上乘上一个值或者除以一个值,或者更加复杂的运算,上面的方法就无法使用了,那么为了解决这个问题就出现了对value更加复杂的操作,例如:3*value+3这种操作,AtomicInteger就为我们提供了这些方法,通过传递一个功能接口,来实现我们想要的,而这个功能接口有一个方法需要我们实现,而复杂的运算就在这个方法中。首先我们看一下这个功能接口:

@FunctionalInterface
public interface IntUnaryOperator {
 /**
 * Applies this operator to the given operand.
 *
 * @param operand the operand
 * @return the operator result
 */ int applyAsInt(int operand);
}
------------------------------
@FunctionalInterface
public interface IntBinaryOperator {
 /**
 * Applies this operator to the given operands.
 *
 * @param left the first operand
 * @param right the second operand
 * @return the operator result
 */ int applyAsInt(int left, int right);
}
 

功能接口一般会有FunctionalInterface注解,说明是一个功能接口,我们通过applyAsInt()方法实现我们的逻辑。那继续看AtomicInteger里面的方法:

//向功能接口方法传递value,并通过CAS+自旋原子操作,然后返回旧值
public final int getAndUpdate(IntUnaryOperator updateFunction) {
 int prev, next;
 do {
 prev = get();
 next = updateFunction.applyAsInt(prev);
 } while (!compareAndSet(prev, next));
 return prev;
}
//向功能接口方法传递value,并通过CAS+自旋原子操作,然后返回新值
public final int updateAndGet(IntUnaryOperator updateFunction) {
 int prev, next;
 do {
 prev = get();
 next = updateFunction.applyAsInt(prev);
 } while (!compareAndSet(prev, next));
 return next;
}
//向功能接口方法传递value和指定的x,并通过CAS+自旋原子操作,然后返回旧值
public final int getAndAccumulate(int x,
 IntBinaryOperator accumulatorFunction) {
 int prev, next;
 do {
 prev = get();
 next = accumulatorFunction.applyAsInt(prev, x);
 } while (!compareAndSet(prev, next));
 return prev;
}
//向功能接口方法传递value和指定的x,并通过CAS+自旋原子操作,然后返回新值
public final int accumulateAndGet(int x,
 IntBinaryOperator accumulatorFunction) {
 int prev, next;
 do {
 prev = get();
 next = accumulatorFunction.applyAsInt(prev, x);
 } while (!compareAndSet(prev, next));
 return next;
}
 

有的同学还是不理解这个功能接口,我对这一类举一个例子:

public class AtomicIntegerTest1 {
 private static AtomicInteger ai = new AtomicInteger(1);
 public static void main(String[] args) {
 //将原来值扩大到原来的2倍
 ai.getAndUpdate(new IntUnaryOperator() {
 @Override
 public int applyAsInt(int operand) {
 return operand * 2;
 }
 });
 System.out.println("ai扩大为原来的2倍:" + ai.get());
 //将原来的值扩大原来3倍,在减去指定的x
 //left:就是value
 //right:就是我们指定的10
 ai.accumulateAndGet(10, new IntBinaryOperator() {
 @Override
 public int applyAsInt(int left, int right) {
 return 3 * left - right;
 }
 });
 System.out.println("ai扩大为原来的3倍,在减去指定的值:" + ai.get());
 }
}
 

运行结果如下:

通过上面的运行结果,大家是不是理解了这一类方法的作用。

第四类操作:对Number方法的实现

public int intValue() {
 return get();
}
public long longValue() {
 return (long)get();
}
public float floatValue() {
 return (float)get();
}
public double doubleValue() {
 return (double)get();
}
 

这一类方法非常的简单了,都是调用get()获取value,然后转化成不同的基本类型。

上面就是AtomicInteger的全面内容了,通过上面的讲解,大家是否对AtomicInteger有了一个更加清晰的理解呢?如有什么不明白的地方,欢迎留言交流。

如果对你有所帮助,请续关注并转发。感谢支持

文章来源:智云一二三科技

文章标题:Java并发包-atomic包-让您彻底掌握AtomicInteger源码

文章地址:https://www.zhihuclub.com/184151.shtml

关于作者: 智云科技

热门文章

网站地图