此文已由作者赵计刚授权网易云社区发布。

欢迎访问网易云社区,了解更多网易技术产品运营经验。


1、原子类

  • 可以实现一些原子操作

  • 基于CAS

下面就以AtomicInteger为例。

2、AtomicInteger

在没有AtomicInteger之前,对于一个Integer的线程安全操作,是需要使用同步锁来实现的,当然现在也可以通过ReentrantLock来实现,但是最好最方便的实现方式是采用AtomicInteger。

具体示例:

package com.collection.test;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * 原子类的测试
 */
public class AtomicTest {
    private static AtomicInteger atomicInteger = new AtomicInteger();
    
    //获取当前值
    public static void getCurrentValue(){
        System.out.println(atomicInteger.get());//-->0
    }
    
    //设置value值
    public static void setValue(){
        atomicInteger.set(12);//直接用12覆盖旧值
        System.out.println(atomicInteger.get());//-->12
    }
    
    //根据方法名称getAndSet就知道先get,则最后返回的就是旧值,如果get在后,就是返回新值
    public static void getAndSet(){
        System.out.println(atomicInteger.getAndSet(15));//-->12
    }
    
    public static void getAndIncrement(){
        System.out.println(atomicInteger.getAndIncrement());//-->15
    }
    
    public static void getAndDecrement(){
        System.out.println(atomicInteger.getAndDecrement());//-->16
    }
    
    public static void getAndAdd(){
        System.out.println(atomicInteger.getAndAdd(10));//-->15
    }
    
    public static void incrementAndGet(){
        System.out.println(atomicInteger.incrementAndGet());//-->26
    }
    
    public static void decrementAndGet(){
        System.out.println(atomicInteger.decrementAndGet());//-->25
    }
    
    public static void addAndGet(){
        System.out.println(atomicInteger.addAndGet(20));//-->45
    }
    
    public static void main(String[] args) {
        AtomicTest test = new AtomicTest();
        test.getCurrentValue();
        test.setValue();
        //返回旧值系列
        test.getAndSet();
        test.getAndIncrement();
        test.getAndDecrement();
        test.getAndAdd();
        //返回新值系列
        test.incrementAndGet();
        test.decrementAndGet();
        test.addAndGet();
        
    }
}

源代码:

    private volatile int value;// 初始化值

    /**
     * 创建一个AtomicInteger,初始值value为initialValue
     */
    public AtomicInteger(int initialValue) {
        value = initialValue;
    }     /**
     * 创建一个AtomicInteger,初始值value为0
     */
    public AtomicInteger() {
    }     /**
     * 返回value
     */
    public final int get() {
        return value;
    }     /**
     * 为value设值(基于value),而其他操作是基于旧值<--get()
     */
    public final void set(int newValue) {
        value = newValue;
    }     public final boolean compareAndSet(int expect, int update) {
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }
    
    /**
     * 基于CAS为旧值设定新值,采用无限循环,直到设置成功为止
     * 
     * @return 返回旧值
     */
    public final int getAndSet(int newValue) {
        for (;;) {
            int current = get();// 获取当前值(旧值)
            if (compareAndSet(current, newValue))// CAS新值替代旧值
                return current;// 返回旧值
        }
    }     /**
     * 当前值+1,采用无限循环,直到+1成功为止
     * @return the previous value 返回旧值
     */
    public final int getAndIncrement() {
        for (;;) {
            int current = get();//获取当前值
            int next = current + 1;//当前值+1
            if (compareAndSet(current, next))//基于CAS赋值
                return current;
        }
    }     /**
     * 当前值-1,采用无限循环,直到-1成功为止 
     * @return the previous value 返回旧值
     */
    public final int getAndDecrement() {
        for (;;) {
            int current = get();
            int next = current - 1;
            if (compareAndSet(current, next))
                return current;
        }
    }     /**
     * 当前值+delta,采用无限循环,直到+delta成功为止 
     * @return the previous value  返回旧值
     */
    public final int getAndAdd(int delta) {
        for (;;) {
            int current = get();
            int next = current + delta;
            if (compareAndSet(current, next))
                return current;
        }
    }     /**
     * 当前值+1, 采用无限循环,直到+1成功为止
     * @return the updated value 返回新值
     */
    public final int incrementAndGet() {
        for (;;) {
            int current = get();
            int next = current + 1;
            if (compareAndSet(current, next))
                return next;//返回新值
        }
    }     /**
     * 当前值-1, 采用无限循环,直到-1成功为止 
     * @return the updated value 返回新值
     */
    public final int decrementAndGet() {
        for (;;) {
            int current = get();
            int next = current - 1;
            if (compareAndSet(current, next))
                return next;//返回新值
        }
    }     /**
     * 当前值+delta,采用无限循环,直到+delta成功为止  
     * @return the updated value 返回新值
     */
    public final int addAndGet(int delta) {
        for (;;) {
            int current = get();
            int next = current + delta;
            if (compareAndSet(current, next))
                return next;//返回新值
        }
    }     /**
     * 获取当前值
     */
    public int intValue() {
        return get();
    }

说明:使用与源代码都简单到爆了!自己看看注释。

注意:

  • value是volatile的,关于volatile的相关内容见《附2 volatile》,具体链接:http://www.cnblogs.com/java-zhao/p/5125698.html

  • 单步操作:例如set()是直接对value进行操作的,不需要CAS,因为单步操作就是原子操作。

  • 多步操作:例如getAndSet(int newValue)是两步操作-->先获取值,在设置值,所以需要原子化,这里采用CAS实现。

  • 对于方法是返回旧值还是新值,直接看方法是以get开头(返回旧值)还是get结尾(返回新值)就好

  • CAS:比较CPU内存上的值是不是当前值current,如果是就换成新值update,如果不是,说明获取值之后到设置值之前,该值已经被别人先一步设置过了,此时如果自己再设置值的话,需要在别人修改后的值的基础上去操作,否则就会覆盖别人的修改,所以这个时候会直接返回false,再进行无限循环,重新获取当前值,然后再基于CAS进行加减操作。

  • 如果还是不懂CAS,类比数据库的乐观锁

补充一个东西:

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

这是AtomicInteger的所有属性,其中value存的是当前值,而当前值存放的内存地址可以通过valueOffset来确定。实际上是“value字段相对Java对象的起始地址的偏移量”

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

CAS方法:通过对比“valueOffset上的value”与expect是否相同,来决定是否修改value值为update值。

免费领取验证码、内容安全、短信发送、直播点播体验包及云服务器等套餐

更多网易技术、产品、运营经验分享请点击

相关文章:
【推荐】 Android中Textview显示Html,图文混排,支持图片点击放大
【推荐】 结合jenkins以及PTP平台的性能回归测试
【推荐】 视觉设计师的进化

AtomicInteger源码解析的更多相关文章

  1. lesson8:AtomicInteger源码解析及性能分析

    AtomicInteger等对象出现的目的主要是为了解决在多线程环境下变量计数的问题,例如常用的i++,i--操作,它们不是线程安全的,AtomicInteger引入后,就不必在进行i++和i--操作 ...

  2. 第十一章 AtomicInteger源码解析

    1.原子类 可以实现一些原子操作 基于CAS 下面就以AtomicInteger为例. 2.AtomicInteger 在没有AtomicInteger之前,对于一个Integer的线程安全操作,是需 ...

  3. LinkedBlockingQueue源码解析(3)

    此文已由作者赵计刚授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 4.3.public E take() throws InterruptedException 原理: 将队 ...

  4. 第九章 LinkedBlockingQueue源码解析

    1.对于LinkedBlockingQueue需要掌握以下几点 创建 入队(添加元素) 出队(删除元素) 2.创建 Node节点内部类与LinkedBlockingQueue的一些属性 static ...

  5. 多线程爬坑之路-J.U.C.atomic包下的AtomicInteger,AtomicLong等类的源码解析

    Atomic原子类:为基本类型的封装类Boolean,Integer,Long,对象引用等提供原子操作. 一.Atomic包下的所有类如下表: 类摘要 AtomicBoolean 可以用原子方式更新的 ...

  6. [Java多线程]-J.U.C.atomic包下的AtomicInteger,AtomicLong等类的源码解析

    Atomic原子类:为基本类型的封装类Boolean,Integer,Long,对象引用等提供原子操作. 一.Atomic包下的所有类如下表: 类摘要 AtomicBoolean 可以用原子方式更新的 ...

  7. Android -- AsyncTask源码解析

    1,前段时间换工作的时候,关于AsyncTask源码这个点基本上大一点的公司都会问,所以今天就和大家一起来总结总结.本来早就想写这篇文章的,当时写<Android -- 从源码解析Handle+ ...

  8. ThreadPoolExecutor系列<三、ThreadPoolExecutor 源码解析>

    本文系作者原创,转载请注明出处:http://www.cnblogs.com/further-further-further/p/7681826.html 在源码解析前,需要先理清线程池控制的运行状态 ...

  9. LinkedBlockingQueue源码解析

    上一篇博客,我们介绍了ArrayBlockQueue,知道了它是基于数组实现的有界阻塞队列,既然有基于数组实现的,那么一定有基于链表实现的队列了,没错,当然有,这就是我们今天的主角:LinkedBlo ...

随机推荐

  1. 过滤输入htmlentities与htmlspecialchars用法

    过滤输入 (即来自所列数据源中的任何数据)是指,转义或删除不安全的字符.在数据到达应用的存储层之前,一定要过滤输入数据.这是第一道防线.假如网站的评论表单接收html,默认情况下 访客可以毫无阻拦地在 ...

  2. MQ java 基础编程

    MQ java 基础编程 编写人:邬文俊 编写时间 : 2006-2-16 联系邮件 : wenjunwu430@gmail.com 前言 通过 2 个多星期对 MQ 学习,在 partner 丁 & ...

  3. web服务器部署过程记录

    由于之前没有服务器部署经验,又选择了所有软件都是单独编译安装,遇到很多问题,解决之后还是学习到了很多新东西. 如今回过头来还是选择lnmp集成环境的部署方式比较方便快捷:https://lnmp.or ...

  4. 基于KVM的qemu中宿主机和虚拟机间的通信

    qga是一个运行在虚拟机内部的普通应用程序(可执行文件名称默认为qemu-ga,服务名称默认为qemu-guest-agent),其目的是实现一种宿主机和虚拟机进行交互的方式,这种方式不依赖于网络,而 ...

  5. Python使用wxPython、py2exe编写桌面程序-乾颐堂

    Python是支持可视化编程,即编写gui程序,你可以用它来编写自己喜欢的桌面程序.使用wxPython来做界面非常的简单,只是不能像C#一样拖动控件,需要自行写代码布局.在完成编写之后,由于直接的p ...

  6. APP自动化测试怎么测?

    一般来说工具还有技术都是千篇一律 测试测到最后还是业务能力...

  7. cuda使用

    import torchx = torch.randn(2, 3)x = x.cuda() print (x)#最简单的cuda使用,但是感觉好慢啊

  8. yii框架场景的用法

    1.在 model 里面定义一下场景 类名必须是 scenarios() public function scenarios() { return [ 'create' => ['title', ...

  9. hdu-1142(记忆化搜索+dij)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1142 思路:1.不是求最短路径,而是求如果两个点A,B直接相连,且A到终点的距离大于B到终点的距离,求 ...

  10. Jsp+servlet+mysql搭建套路

    1.建立数据库根据需求建立相应的数据库确立数据库的字段.属性.主键等2.建立javaweb项目,搭建开发环境在开发环境的/WebRoot/WEB-INF下建立lib文件夹,存放需要使用的jar包常用的 ...