Java中的原子类AtomicInteger使用详解
作者:努力的小强
原子操作
原子操作是指不会被线程调度机制打断的操作,这种操作一旦开始,就一直运行到结束,中间不会有任何线程上下文切换。
原子操作可以是一个步骤,也可以是多个操作步骤,但是其顺序不可以被打乱,也不可以被切割而只执行其中的一部分,将整个操作视作一个整体是原子性的核心特征。
原子类
在Java中提供了很多原子类,主要把这些原子类分成以下几大类
原子更新基本类型或引用类型
- AtomicBoolean
- 原子更新布尔类型,内部使用int类型的value存储1和0表示true和false,底层也是对int类型的原子操作。
- AtomicInteger
- 原子更新int类型。
- AtomicLong
- 原子更新long类型。
- AtomicReference
- 原子更新引用类型,通过泛型指定要操作的类。
- AtomicMarkableReference
- 原子更新引用类型,内部使用Pair承载引用对象及是否被更新过的标记,避免了ABA问题。
- AtomicStampedReference
- 原子更新引用类型,内部使用Pair承载引用对象及更新的邮戳,避免了ABA问题。
原子更新数组中的元素
原子更新数组中的元素,可以更新数组中指定索引位置的元素。
- AtomicIntegerArray
原子更新int数组中的元素。 - AtomicLongArray
原子更新long数组中的元素。 - AtomicReferenceArray
原子更新object数组中的元素。
原子更新对象中的字段
原子更新对象中的字段,可以更新对象中指定字段名称的字段。
- AtomicIntegerFieldUpdater
原子更新对象中的int类型字段。 - AtomicLongFieldUpdater
原子更新对象中的long类型字段。 - AtomicReferenceFieldUpdater
原子更新对象中的引用类型字段。
高性能原子类
高性能原子类,是Java8中增加的原子类,它们使用分段的思想,把不同线程hash到不同的段上去更新,最后再把这些段的值相加得到最终的值。
Striped64
下面四个类的父类。
- LongAccumulator
- long类型的聚合器,需要传入一个long类型的二元操作,可以用来计算各种聚合操作,包括加乘等。
- LongAdder
- long类型聚合器,LongAccumulator的特例,只能用来计算加法,且从零开始计算。
- DoubleAccumulator
- double类型的聚合器,需要传入一个double类型的二元操作,可以用来计算各种聚合操作,包括加乘等。
- DoubleAdder
- double类型聚合器,DoubleAccumulator的特例,只能用来计算加法,且从零开始计算。
AtomicInteger使用
import java.util.concurrent.atomic.AtomicInteger; public class AtomicIntegerDemo { public static void main(String[] args) throws InterruptedException { test1(); test2(); } private static void test1() throws InterruptedException { Counter counter = new Counter(); for (int i = 0; i < 10; i++) { new Thread(()->{ for (int j = 0; j < 1000; j++) { counter.addCount(); } }).start(); } Thread.sleep(1000); System.out.println("test1 count = " + counter.getCount()); } private static void test2() throws InterruptedException { AtomicInteger count = new AtomicInteger(); for (int i = 0; i < 10; i++) { new Thread(()->{ for (int j = 0; j < 1000; j++) { count.incrementAndGet(); } }).start(); } Thread.sleep(1000); System.out.println("test2 count = " + count.get()); } } public class Counter { private volatile static int count = 0; public void addCount(){ count++; } public int getCount(){ return count; } }
运行以上代码会发现,test1的结果达不到预期的10000,而且每次的结果不可再现。而test2的结果每次都是10000,是确定的可再现的。
是因为test1中调用的Counter类的addCount方法,这个方法不是原子性的。
count++可以分解为以下几个原子性的步骤:
1.读取count的值
2.计算新值count+1
3.新值写入count变量
如果步骤1、2、3中有多个线程并发执行,那么就会出现两个或多个线程并发的执行+1操作,而我们希望的是每个线程依次执行+1的操作。
AtomicInteger原理
AtomicInteger声明
public class AtomicInteger extends Number implements java.io.Serializable
Unsafe类的使用
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); } }
AtomicInteger属性
private volatile int value;
AtomicInteger构造器
public AtomicInteger(int initialValue) { value = initialValue; } public AtomicInteger() { }
AtomicInteger自增
public final int incrementAndGet() { return unsafe.getAndAddInt(this, valueOffset, 1) + 1; }
调用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; }
compareAndSwapInt即CAS方式修改int值:
- 调用unsafe.getAndAddInt方法。
- unsafe.getAndAddInt方法通过自旋的方式,每次尝试通过CAS方式对原值进行累加。如果累加失败,将进入下一次循环。如果累加成功,则自选结束。
到此这篇关于Java中的原子类AtomicInteger使用详解的文章就介绍到这了,更多相关Java原子类AtomicInteger内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!