AtomicInteger原理&源码分析

转自https://www.cnblogs.com/rever/p/8215743.html

深入解析Java AtomicInteger原子类型

在进行并发编程的时候我们需要确保程序在被多个线程并发访问时可以得到正确的结果,也就是实现线程安全。线程安全的定义如下:

当多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些线程将如何交替执行,并且在主调代码中不需要任何额外的同步或协同,这个类都能表现出正确的行为,那么这个类就是线程安全的。

举个线程不安全的例子。假如我们想实现一个功能来统计网页访问量,你可能想到用count++ 来统计访问量,但是这个自增操作不是线程安全的。count++ 可以分成三个操作:

  1. 获取变量当前值
  2. 给获取的当前变量值+1
  3. 写回新的值到变量

假设count的初始值为10,当进行并发操作的时候,可能出现线程A和线程B都进行到了1操作,之后又同时进行2操作。A先进行到3操作+1,现在值为11;注意刚才AB获取到的当前值都是10,所以B执行3操作后,count的值依然是11。这个结果显然不符合我们的要求。

所以我们需要用本篇的主角—— AtomicInteger 来保证线程安全。

AtomicInteger 的源码如下:

package java.util.concurrent.atomic;
import sun.misc.Unsafe;

public class AtomicInteger extends Number implements java.io.Serializable {
    private static final long serialVersionUID = 6214790243416807050L;

    // setup to use Unsafe.compareAndSwapInt for updates
    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); }
    }

    private volatile int value;

    public AtomicInteger(int initialValue) {
        value = initialValue;
    }

    public AtomicInteger() {
    }

    public final int get() {
        return value;
    }

    public final void set(int newValue) {
        value = newValue;
    }

    public final void lazySet(int newValue) {
        unsafe.putOrderedInt(this, valueOffset, newValue);
    }

    public final int getAndSet(int newValue) {
        for (;;) {
            int current = get();
            if (compareAndSet(current, newValue))
                return current;
        }
    }

    public final boolean compareAndSet(int expect, int update) {
    return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }

    public final boolean weakCompareAndSet(int expect, int update) {
    return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }

    public final int getAndIncrement() {
        for (;;) {
            int current = get();
            int next = current + 1;
            if (compareAndSet(current, next))
                return current;
        }
    }

    public final int getAndDecrement() {
        for (;;) {
            int current = get();
            int next = current - 1;
            if (compareAndSet(current, next))
                return current;
        }
    }

    public final int getAndAdd(int delta) {
        for (;;) {
            int current = get();
            int next = current + delta;
            if (compareAndSet(current, next))
                return current;
        }
    }

    public final int incrementAndGet() {
        for (;;) {
            int current = get();
            int next = current + 1;
            if (compareAndSet(current, next))
                return next;
        }
    }

    public final int decrementAndGet() {
        for (;;) {
            int current = get();
            int next = current - 1;
            if (compareAndSet(current, next))
                return next;
        }
    }

    public final int addAndGet(int delta) {
        for (;;) {
            int current = get();
            int next = current + delta;
            if (compareAndSet(current, next))
                return next;
        }
    }

    public String toString() {
        return Integer.toString(get());
    }

    public int intValue() {
    return get();
    }

    public long longValue() {
    return (long)get();
    }

    public float floatValue() {
    return (float)get();
    }

    public double doubleValue() {
    return (double)get();
    }

}


我们先看原子整型类中定义的属性

   // setup to use Unsafe.compareAndSwapInt for updates
    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); }
    }

Unsafe是JDK内部的工具类,主要实现了平台相关的操作。下面内容引自

sun.misc.Unsafe是JDK内部用的工具类。它通过暴露一些Java意义上说“不安全”的功能给Java层代码,来让JDK能够更多的使用Java代码来实现一些原本是平台相关的、需要使用native语言(例如C或C++)才可以实现的功能。该类不应该在JDK核心类库之外使用。

Unsafe的具体实现跟本篇的目标关联不大,你只要知道这段代码是为了获取value在堆内存中的偏移量就够了。偏移量在AtomicInteger中很重要,原子操作都靠它来实现。

Value的定义和volatile

AtomicInteger 本身是个整型,所以最重要的属性就是value,我们看看它是如何声明value的

 private volatile int value;

我们看到value使用了volatile修饰符,那么什么是volatile呢?

volatile相当于synchronized的弱实现,也就是说volatile实现了类似synchronized的语义,却又没有锁机制。它确保对volatile字段的更新以可预见的方式告知其他的线程。

volatile包含以下语义:

  1. Java 存储模型不会对valatile指令的操作进行重排序:这个保证对volatile变量的操作时按照指令的出现顺序执行的。
  2. volatile变量不会被缓存在寄存器中(只有拥有线程可见)或者其他对CPU不可见的地方,每次总是从主存中读取volatile变量的结果。也就是说对于volatile变量的修改,其它线程总是可见的,并且不是使用自己线程栈内部的变量。也就是在happens-before法则中,对一个valatile变量的写操作后,其后的任何读操作理解可见此写操作的结果。

简而言之volatile 的作用是当一个线程修改了共享变量时,另一个线程可以读取到这个修改后的值。在分析AtomicInteger 源码时,我们了解到这里就足够了。

用CAS操作实现安全的自增

AtomicInteger中有很多方法,例如incrementAndGet() 相当于i++ 和getAndAdd() 相当于i+=n 。从源码中我们可以看出这几种方法的实现很相似,所以我们主要分析incrementAndGet() 方法的源码。

源码如下:

 public final int incrementAndGet() {
        for (;;) {
            int current = get();
            int next = current + 1;
            if (compareAndSet(current, next))
                return next;
        }
    }

 public final boolean compareAndSet(int expect, int update) {
    return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }

incrementAndGet() 方法实现了自增的操作。核心实现是先获取当前值和目标值(也就是value+1),如果compareAndSet(current, next) 返回成功则该方法返回目标值。那么compareAndSet是做什么的呢?理解这个方法我们需要引入CAS操作。

在大学操作系统课程中我们学过独占锁和乐观锁的概念。独占锁就是线程获取锁后其他的线程都需要挂起,直到持有独占锁的线程释放锁;乐观锁是先假定没有冲突直接进行操作,如果因为有冲突而失败就重试,直到操作成功。其中乐观锁用到的机制就是CAS,Compare and Swap。

AtomicInteger 中的CAS操作就是compareAndSet(),其作用是每次从内存中根据内存偏移量(valueOffset)取出数据,将取出的值跟expect 比较,如果数据一致就把内存中的值改为update。

这样使用CAS就保证了原子操作。其余几个方法的原理跟这个相同,在此不再过多的解释。

没看AtomicInteger 源码之前,我认为其内部是用synchronized 来实现的原子操作。查阅资料后发现synchronized 会影响性能,因为Java中的synchronized 锁是独占锁,虽然可以实现原子操作,但是这种实现方式的并发性能很差。

总结

总结一下,AtomicInteger 中主要实现了整型的原子操作,防止并发情况下出现异常结果,其内部主要依靠JDK 中的unsafe 类操作内存中的数据来实现的。volatile 修饰符保证了value在内存中其他线程可以看到其值得改变。CAS操作保证了AtomicInteger 可以安全的修改value 的值。

原文地址:https://www.cnblogs.com/heroinss/p/10640860.html

时间: 2024-08-02 08:26:23

AtomicInteger原理&源码分析的相关文章

[五]类加载机制双亲委派机制 底层代码实现原理 源码分析 java类加载双亲委派机制是如何实现的

Launcher启动类 本文是双亲委派机制的源码分析部分,类加载机制中的双亲委派模型对于jvm的稳定运行是非常重要的 不过源码其实比较简单,接下来简单介绍一下 我们先从启动类说起 有一个Launcher类   sun.misc.Launcher; 仔细看下这简短的几行注释,可以得到有用的信息 ps:直接IDE里面查看反编译的,看不到注释的,可以下载openJDK查看源码,我的这个版本是openjdk-8-src-b132-03_mar_2014 sun.misc.Launcher这个类是系统用于

Mybatis Interceptor 拦截器原理 源码分析

Mybatis采用责任链模式,通过动态代理组织多个拦截器(插件),通过这些拦截器可以改变Mybatis的默认行为(诸如SQL重写之类的),由于插件会深入到Mybatis的核心,因此在编写自己的插件前最好了解下它的原理,以便写出安全高效的插件. 代理链的生成 Mybatis支持对Executor.StatementHandler.PameterHandler和ResultSetHandler进行拦截,也就是说会对这4种对象进行代理. 通过查看Configuration类的源代码我们可以看到,每次都

源码分析—ThreadPoolExecutor线程池三大问题及改进方案

前言 在一次聚会中,我和一个腾讯大佬聊起了池化技术,提及到java的线程池实现问题,我说这个我懂啊,然后巴拉巴拉说了一大堆,然后腾讯大佬问我说,那你知道线程池有什么缺陷吗?我顿时哑口无言,甘拜下风,所以这次我再回来思考一下线程池的实现原理 源码分析 ThreadPoolExecutor构造器 public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, Blo

ReentrantReadWriteLock源码分析

概述 ReentrantReadWriteLock维护了一对相关的锁,它们分别是共享readLock和独占writeLock.关于共享读锁和排他写锁的概念其实很好理解.所谓共享读锁就是一个线程读的时候,其它线程也可以来读(共享),但是不能来写.排他写锁是指一个线程在写的时候,其它线程不能来写或读(排他).除了这个特点之外,ReentrantReadWriteLock还有一个特点就是可重入的.它和ReentrantLock一样都是支持Condition的.而且ReentrantReadWerite

JDK源码分析-AtomicInteger

AtomicInteger可以看做Integer类的原子操作工具类.在java.util.concurrent.atomic包下,在一些使用场合下可以取代加锁操作提高并发性.接下来就从几个方面来介绍: 1.原子性和CAS. 2.CPU底层实现原理. 3.atomic包介绍. 4.源码分析. 原子性和CAS 原子性就是指某一个操作是不可拆分的,是一个整体必须要一次性全部执行完成要么就不执行. CAS是Compare And Swap(比较并交换).意思是当你要更新某个值的时候先要检查这个变量的当前

AtomicInteger源码分析——基于CAS的乐观锁实现

AtomicInteger源码分析--基于CAS的乐观锁实现 1. 悲观锁与乐观锁 我们都知道,cpu是时分复用的,也就是把cpu的时间片,分配给不同的thread/process轮流执行,时间片与时间片之间,需要进行cpu切换,也就是会发生进程的切换.切换涉及到清空寄存器,缓存数据.然后重新加载新的thread所需数据.当一个线程被挂起时,加入到阻塞队列,在一定的时间或条件下,在通过notify(),notifyAll()唤醒回来.在某个资源不可用的时候,就将cpu让出,把当前等待线程切换为阻

死磕 java并发包之AtomicInteger源码分析

问题 (1)什么是原子操作? (2)原子操作和数据库的ACID有啥关系? (3)AtomicInteger是怎么实现原子操作的? (4)AtomicInteger是有什么缺点? 简介 AtomicInteger是java并发包下面提供的原子类,主要操作的是int类型的整型,通过调用底层Unsafe的CAS等方法实现原子操作. 还记得Unsafe吗?点击链接直达[死磕 java魔法类之Unsafe解析] 原子操作 原子操作是指不会被线程调度机制打断的操作,这种操作一旦开始,就一直运行到结束,中间不

JUC源码分析-集合篇(五)BlockingQueue 阻塞式队列实现原理

JUC源码分析-集合篇(五)BlockingQueue 阻塞式队列实现原理 以 LinkedBlockingQueue 分析 BlockingQueue 阻塞式队列的实现原理. 1. 数据结构 LinkedBlockingQueue 和 ConcurrentLinkedQueue 一样都是由 head 节点和 last 节点组成,每个节点(Node)由节点元素(item)和指向下一个节点(next)的引用组成,节点与节点之间就是通过这个 next 关联起来,从而组成一张链表结构的队列.默认情况下

Hadoop之HDFS原理及文件上传下载源码分析(上)

HDFS原理 首先说明下,hadoop的各种搭建方式不再介绍,相信各位玩hadoop的同学随便都能搭出来. 楼主的环境: 操作系统:Ubuntu 15.10 hadoop版本:2.7.3 HA:否(随便搭了个伪分布式) 文件上传 下图描述了Client向HDFS上传一个200M大小的日志文件的大致过程: 首先,Client发起文件上传请求,即通过RPC与NameNode建立通讯. NameNode与各DataNode使用心跳机制来获取DataNode信息.NameNode收到Client请求后,