1 前言
这节我们从AtomicInteger这个比较简单的原子类开始,来看看AtomicInteger的底层原理。
2 实测样例对比线程安全性
在说AtomicInteger的底层原理之前呢,我们先来看个例子感受下原子类:
static修饰的共享变量,我们开启两个线程对共享变量进行10000次+1的操作
2.1 Integer的测试样例
// 继承线程类 public class AtomicTest extends Thread { // 类变量看作共享变量 private static int num = 0; // 线程工作内容 执行10000次+1 @Override public void run() { for (int i = 0; i < 10000; i++) { num++; } } // main方法 public static void main(String[] args) throws InterruptedException { // 两个线程分别去对共享变量执行10000次+1 AtomicTest demo1 = new AtomicTest(); AtomicTest demo2 = new AtomicTest(); demo1.start(); demo2.start(); // 等待两个线程都执行完 demo1.join(); demo2.join(); // 打印共享变量 System.out.println("num = " + AtomicTest.num); } }
2.2 AtomicInteger的测试样例
// 继承线程类 public class AtomicTest extends Thread { // 类变量看作共享变量 private static AtomicInteger num = new AtomicInteger(0); // 线程工作内容 执行10000次+1 @Override public void run() { for (int i = 0; i < 10000; i++) { num.addAndGet(1); } } // main方法 public static void main(String[] args) throws InterruptedException { // 两个线程分别去对共享变量执行10000次+1 AtomicTest demo1 = new AtomicTest(); AtomicTest demo2 = new AtomicTest(); demo1.start(); demo2.start(); // 等待两个线程都执行完 demo1.join(); demo2.join(); // 打印共享变量 System.out.println("num = " + AtomicTest.num); } }
3 AtomicInteger原理
我们先通过源码来看一下AtomicInteger内部有哪些属性以及作用是什么:
public class AtomicInteger extends Number implements java.io.Serializable { // unsafe对象,可以直接根据内存地址操作数据,可以突破java语法的限制 private static final Unsafe unsafe = Unsafe.getUnsafe(); // 存储实际的值 private volatile int value; // 存储value属性在AtomicInteger类实例内部的偏移地址 private static final long valueOffset; static { try { // 在类初始化的时候就获取到了value变量在对象内部的偏移地址 valueOffset = unsafe.objectFieldOffset (AtomicInteger.class.getDeclaredField("value")); } catch (Exception ex) { throw new Error(ex); } } }
(1)首先内部持有一个unsafe对象的实例,Atomic原子类底层的操作都是基于unsafe对象来进行的
(2)然后有一个volatile int value变量,这个value就是原子类的实际数值,使用volatile来修饰,volatile可以保证并发中的可见性和有序性(这里之前讲过volatile可以保证可见性和有序性,不记得的要回去重新看一下哦)
(3)还有一个valueOffset,看看这段代码,其实就是获得value属性在AtomicInteger对象内部的偏移地址的:
valueOffset = unsafe.objectFieldOffset (AtomicInteger.class.getDeclaredField("value"));
这个value属性相对于AtomicInter对象的内部偏移量存储在valueOffset中,我们之前讲过的,通过unsafe类是直接在内存级别去给变量赋值的。这里啊,我们再回顾一下unsafe值怎么从内存级别操作数据的:
首先要知道你要操作对象的内存地址,也就是AtomicInteger对象引用指向的内存地址
其次是要知道value属性在对象内部的偏移量offset,就可以通过(对象地址 + offset偏移量)直接找到value变量在内存的地址是多少,然后就可以直接给这块内存赋值了。
可以看到AtomicInteger内部其实就是一个 volatile int value的属性、一个unsafe类、一个偏移地址就完事了,Atomic原子类就是对基础的类型进行了一下包装而已,使得他们是线程安全的。比如AtomicInteger要对int进行包装,它内部是有一个属性来存储int的值的。至于它其他两个属性valueOffset、unsafe是辅助实现并发安全的属性。
3.1 AtomicInteger的构造方法
我们再来看看AtomicInteger的构造方法源码:
public AtomicInteger(int initialValue) { value = initialValue; } public AtomicInteger() { }
提供了两个构造方法,第一个是在创建AtomicInteger对象的时候直接给内存存储值的volatile int value设置初始化的值;
第二个没有赋初始值,那默认就是0;
3.2 AtomicInteger方法的源码分析
3.2.1 getAndIncrement()方法源码
public final int getAndIncrement() { return unsafe.getAndAddInt(this, valueOffset, 1); }
我们看到AtomicInteger的getAndIncrement()方法源码很简单,底层就是基于unsafe.getAndAddInt包装了一下,让我们继续看一下unsafe.getAndAddInt方法源码:
public final int getAndAddInt(Object o, long valueOffset, int x) { int expected; do { expected = this.getIntVolatile(o, valueOffset); } while(!this.compareAndSwapInt(o, valueOffset, expected, expected + x)); return expected; }
(1)首先(o + valueOffset)得到value变量在内存中的地址,然后根据地址直接取出value在主内存值,这个值记录为expected中
(2)根据 (o + offsetSet)地址偏移量,expected期待的值跟当前内存的值进行对比,如果相等则CAS操作成功,内存的值修改为 expected + x
(3)如果值不相等,则进入下一次循环,直到CAS操作成功为止。
(4)由于使用了volatile 修饰符修饰了value,所以一旦修改了别的线程能立马可见、同时volatile还是用内存屏障确保有序性
(5)所以上面的CAS操作确保了原子性,通过volatile确保可见性、有序性;线程安全的三个特性都满足了,上面的操作就是线程安全的。
3.2.2 compareAndSet()方法源码
public final boolean compareAndSet(int expect, int update) { return unsafe.compareAndSwapInt(this, valueOffset, expect, update); }
底层也还是直接调用unsafe的compareAndSwapInt方法直接去修改,不过这里不同的是,只会执行一次CAS操作,即使失败了也不会重复CAS
3.2.3 其它方法源码
其它的方法,基本都是直接调用unsafe.getAndInt方法:
public final int getAndDecrement() { return unsafe.getAndAddInt(this, valueOffset, -1); } public final int getAndAdd(int delta) { return unsafe.getAndAddInt(this, valueOffset, delta); }
4 小结
AtomicInteger基本都是调用unsafe的CAS操作确保原子性,然后使用volatile修饰变量,确保可见性和有序性,有理解不对的地方欢迎指正哈。
标签:Java,int,unsafe,value,AtomicInteger,线程,Atomic,public From: https://www.cnblogs.com/kukuxjx/p/17284757.html