Java

一、为什么引入 AtomicInteger ?

谈到线程安全,会首先想到了synchronized 和 Lock,但是这种方式又有一个名字,叫做互斥锁,一次只能有一个持有锁的线程进入,再加上还有不同线程争夺锁这个机制,效率比较低,所以又称 悲观锁
与之相对应,就有了 乐观锁 的概念:它不加锁去完成某项操作,如果因为冲突失败就重试,直到成功为止。
AtomicInteger 保证线程安全就是使用了乐观锁,所以相对于悲观锁,效率更高。
在有多个线程同时使用CAS操作一个变量时,只有一个会胜出并成功更新,其余均会失败。失败的线程不会被挂起,仅被告知失败,并且允许再次尝试,当然,也允许失败的线程放弃操作

二、AtomicInteger 原理分析

1. 具体使用

假如想实现一个功能来统计网页访问量,可以使用 count++ 来统计访问量,但是这个自增操作不是线程安全的。
加锁实现:

  1. class Counter {
  2. private volatile int count = 0;
  3. public synchronized void increment() {
  4. count++;
  5. }
  6. public int getCount() {
  7. return count;
  8. }
  9. }

AtomicInteger 实现:

  1. class Counter {
  2. //使用AtomicInteger之后,不需要加锁,也可以实现线程安全。
  3. private AtomicInteger count = new AtomicInteger();
  4. public void increment() {
  5. count.incrementAndGet();
  6. }
  7. public int getCount() {
  8. return count.get();
  9. }
  10. }

2. 原理分析

AtomicInteger 类中定义的属性:

  1. // setup to use Unsafe.compareAndSwapInt for updates
  2. private static final Unsafe unsafe = Unsafe.getUnsafe();
  3. private static final long valueOffset;
  4. static {
  5. try {
  6. valueOffset = unsafe.objectFieldOffset
  7. (AtomicInteger.class.getDeclaredField("value"));
  8. } catch (Exception ex) { throw new Error(ex); }
  9. }

Unsafe是JDK内部的工具类,主要实现了平台相关的操作
sun.misc.Unsafe 是JDK内部用的工具类。它通过暴露一些Java意义上说“不安全”的功能给Java层代码,来让JDK能够更多的使用Java代码来实现一些原本是平台相关的、需要使用native语言(例如C或C++)才可以实现的功能。该类不应该在JDK核心类库之外使用。
简单来说:这段代码是为了获取value在堆内存中的偏移量
Value的定义:

  1. private volatile int value;

volatile 主要特性有两点:防止重排序;实现内存可见性
内存可见性的作用是当一个线程修改了共享变量时,另一个线程可以读取到这个修改后的值
用CAS操作实现原子性:
AtomicInteger中有很多方法,例如

  • incrementAndGet() 相当于i++ ;
  • getAndAdd() 相当于i+=n 。

从源码中可以看出这几种方法的实现很相似,这里主要分析incrementAndGet()方法的源码

  1. public final int incrementAndGet() {
  2. for (;;) {
  3. int current = get();
  4. int next = current + 1;
  5. if (compareAndSet(current, next))
  6. return next;
  7. }
  8. }
  9. public final boolean compareAndSet(int expect, int update) {
  10. return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
  11. }

incrementAndGet() 方法实现了自增的操作。
核心实现是先获取当前值和目标值(也就是current +1),如果compareAndSet(current, next) 返回成功则该方法返回目标值。
那么compareAndSet是做CAS操作
每次从内存中根据内存偏移量(valueOffset)取出数据,将取出的值跟expect 比较,如果数据一致就把内存中的值改为update,如果数据不一致说明内存中的数据已经有过更新,此时就进行回滚(expect值不生效)操作。
这样使用CAS的方法就保证了原子操作
Java中的 AtomicLongAtomicBoolean 等方法的基本原理和思想跟AtomicInteger基本相同

三、简单解释

(1)用volatile关键字修饰 value 字段,保证了 value 字段对各个线程的可见性,各线程读取value字段时,会先从主从把数据同步到工作内存中,这样保证可见性
(2)Unsafe 实现操作原子性,用户在使用时无需增加额外的同步操作,compareAndSetInt方法是一个CAS操作,用native关键字修饰
原理:先比较内存中的值与expected是否一致,一致的前提下才赋予新的值x,此时返回true,否则返回false