AtomicInteger的incrementAndGet方法源码

众所周知,i++分为三步:

1. 读取i的值

2. 计算i+1

3. 将计算出i+1赋给i

可以使用锁来保持操作的原子性,用volatile保持值的可见性和操作顺序性;

如果仅仅是计算操作,我们自然就想到了java.util.concurrent.atomic包下的原子类,则不必考虑锁的升级、获取、释放等消耗,也不必考虑锁的粒度、种类、可重入性等;

下面来看下AtomicInteger的incrementAndGet方法源码:

public final int incrementAndGet() {
        return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
    }

incrementAndGet,先increment,再get,所以获取的是increment后的值,而unsafe.getAndAddInt先get,所以这里需要"+1";

那这里的valueOffset又是什么呢?

  private static final long valueOffset;

    static {
        try {
            valueOffset = unsafe.objectFieldOffset
                (AtomicInteger.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }

AtomicInteger的静态属性valueOffset,属性value的偏移量,在类加载的时候,通过AtomicInteger的Field——"value"初始化,后续通过当前的AtomicInteger实例和该valueOffset obtain该实例value属性的值;

unsafe.getAndAddInt IDEA反编译后源码如下:

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;
    }

unsafe.getAndAddInt具体实现是不停的compare主存中取到的值var5和当前的线程的工作内存中的值(通过对象var1和偏移量var2获得),直到两者equal则更新为新值var5+var4(delta) ,从这里我们也体会到了Java内存模型。

getIntVolatile(主存)和compareAndSwapInt都是sun.misc.Unsafe的native方法。

下面是一个小例子,分别是非线程安全的i++,锁同步以及Atomic Class:

public class Counter {
    @Getter
    private volatile int i = 0;

    @Getter
    private volatile AtomicInteger atomicInteger = new AtomicInteger(0);

    public void increment(){
        i++;
        // 1. 读取i的值
        // 2. 计算i+1
        // 3. 把i+1的值赋给i
    }

    public void incrementSync(){
        synchronized(this) {
            i++;
            // 1. 读取i的值
            // 2. 计算i+1
            // 3. 把i+1的值赋给i
        }
    }

    public void incrementAtomic(){
        // 先increment再返回
        atomicInteger.incrementAndGet();
    }
}

测试类:

public class AtomicityTest {
    private Counter counter;
    /**
     * 每个线程打印的次数
     */
    private int count;

    @Before
    public void init(){
        counter = new Counter();
        count = 10000;
    }

    /**
     * 非线程安全的i++
     */
    @Test
    public void increment() throws InterruptedException {

        Thread t1 = new Thread(() -> {
            for (int i = 0; i < count; i++) {
                counter.increment();
            }
        });

        Thread t2 = new Thread(() -> {
            for (int i = 0; i < count; i++) {
                counter.increment();
            }
        });

        t1.start();
        t2.start();

        // 单元测试必须新起的线程要在主线程里join,否则主线程运行完毕,新起的线程还执行完
        t1.join();
        t2.join();
        /*
        ThreeParamsEquals<Enum, Enum, Enum> equals = (Enum p1, Enum p2, Enum p3) -> p1.equals(p2) && p1.equals(p3);
        while (true){
            if (equals.equals(Thread.State.TERMINATED, t1.getState(), t2.getState())) {
                break;
            }
        }*/
        System.out.println(t1.getState());
        System.out.println(t2.getState());
        // 由于不是原子性操作,两个线程执行总共20000次i++,结果i的值都小于20000
        System.out.println(counter.getI());
    }

    /**
     * 线程安全的i++
     */
    @Test
    public void incrementSync() throws InterruptedException {

        Thread t1 = new Thread(() -> {
            for (int i = 0; i < count; i++) {
                counter.incrementSync();
            }
        });

        Thread t2 = new Thread(() -> {
            for (int i = 0; i < count; i++) {
                counter.incrementSync();
            }
        });

        t1.start();
        t2.start();

        t1.join();
        t2.join();

        System.out.println(t1.getState());
        System.out.println(t2.getState());
        // 由于保证了原子性,顺序性,可见性操作,两个线程执行总共20000次i++,结果i的值都等于20000
        System.out.println(counter.getI());
    }

    /**
     * 原子类AtomicInteger
     */
    @Test
    public void incrementAtomic() throws InterruptedException {

        Thread t1 = new Thread(() -> {
            for (int i = 0; i < count; i++) {
                counter.incrementAtomic();
            }
        });

        Thread t2 = new Thread(() -> {
            for (int i = 0; i < count; i++) {
                counter.incrementAtomic();
            }
        });

        t1.start();
        t2.start();

        t1.join();
        t2.join();

        System.out.println(t1.getState());
        System.out.println(t2.getState());
        // 由于保证了原子性,顺序性,可见性操作,两个线程执行总共20000次i++,结果i的值都等于20000
        System.out.println(counter.getAtomicInteger());
    }
}

原文地址:https://www.cnblogs.com/theRhyme/p/12129120.html

时间: 2024-10-29 22:02:42

AtomicInteger的incrementAndGet方法源码的相关文章

python实现whois查询功能的方法源码

恐怕很多朋友跟我一样,使用python语言居然能实现whois服务器查询功能.下面我把代码和说明搬来给大家看看,有谁需要可以参考下.本来想直接从whois服务器查询的,但是发现要写socket 用43端口链接服务器,但是有些服务器的地址不清楚,而且查询命令貌似有改变所以不想折腾了,就想着直接用chinaz的页面实现一下算了.如下代码是在 win7下操作的,安装python3.2测试通过. Python3.2实现whois查询功能的方法源码: # -*- coding:utf-8 -*- impo

Android编程之Fragment动画加载方法源码详解

上次谈到了Fragment动画加载的异常问题,今天再聊聊它的动画加载loadAnimation的实现源代码: Animation loadAnimation(Fragment fragment, int transit, boolean enter, int transitionStyle) { 接下来具体看一下里面的源码部分,我将一部分一部分的讲解,首先是: Animation animObj = fragment.onCreateAnimation(transit, enter, fragm

Java split方法源码分析

Java split方法源码分析 1 public String[] split(CharSequence input [, int limit]) { 2 int index = 0; // 指针 3 boolean matchLimited = limit > 0; // 是否限制匹配个数 4 ArrayList<String> matchList = new ArrayList<String>(); // 匹配结果队列 5 Matcher m = matcher(inp

jQuery的getText()方法源码

/** * Utility function for retrieving the text value of an array of DOM nodes * @param {Array|Element} elem */ getText = Sizzle.getText = function( elem ) { var node, ret = "", i = 0, nodeType = elem.nodeType; if ( !nodeType ) { // If no nodeTyp

invalidate和requestLayout方法源码分析

invalidate方法源码分析 在之前分析View的绘制流程中,最后都有调用一个叫invalidate的方法,这个方法是啥玩意?我们来看一下View类中invalidate系列方法的源码(ViewGroup没有重写这些方法),如下: /**  * Mark the area defined by dirty as needing to be drawn. dirty代表需要重新绘制的脏的区域  * If the view is visible, onDraw(Canvas) will be c

TreeSet集合的add()方法源码解析(01.Integer自然排序)

>TreeSet集合使用实例 >TreeSet集合的红黑树 存储与取出(图) >TreeSet的add()方法源码     TreeSet集合使用实例 package cn.itcast_05; import java.util.TreeSet; /* * TreeSet:能够对元素按照某种规则进行排序. * 排序有两种方式 * A:自然排序 * B:比较器排序 * * TreeSet集合的特点:排序和唯一 * * 通过观察TreeSet的add()方法,我们知道最终要看TreeMap的

.NET软件防破解方法源码混淆

其实我们只要在软件设计的关键几个环节,利用专业的控件进行保护,就可以保证软件的安全.我们可以在软件设计时和运行时对软件代码进行保护.在设计时的保护,主要的保护手段是,混淆源码:在运行时的保护主要的手段是加壳程序和授权控制.下面我们分别对混淆.加壳.授权控制的方法和采用的控件做一一讲解. 源码混淆 源码混淆就是通过对程序源码的分析,改变源码的原始面貌,降低源码可读性,可对函数甚至流程进行混淆.虽然目前很多开发工具都能进行简单的混淆,不过实用性不大,采用专业的混淆控件对程序源码能起到有效的保护,有些

jQuery方法源码解析--jQuery($)方法(一)

jQuery方法源码解析--jQuery($)方法 注: 1.本文分析的代码为jQuery.1.11.1版本,在官网上下载未压缩版即可 2.转载请注明出处 jQuery方法: 这个方法大家都不陌生,在使用过程中,它还有另外一个名字,美元符号:$,$(...)其实就是jQuery(...); 它有很多种用法,通常都返回一个jquery对象,也可以作为$(document).ready(...);的简写形式,分析之前先看一下jQuery都有什么用法. 1.jQuery( selector [, co

php将图片保存到mysql数据库及从数据库中读取图片的方法源码 转

php将图片保存到mysql数据库及从数据库中读取图片的方法源码 分类: 网站 2012-03-11 15:25 5059人阅读 评论(0) 收藏 举报 数据库mysqlphpsql serverquerydatabase 一般来讲都是把图片保存到服务器下,然后根据路径读出的,但是有时候出于安全及版权什么的考虑,会把图片保存到mysql的数据库中,然后再读出来,这样的图片点击右键属性,是看不到图片地址的.下面逍遥一生就介绍下如何用php把图片存储到mysql中及如何读出.     MySQL数据