在多线程的场景中,我们需要保证数据安全,就会考虑同步的方案,通常会使用synchronized或者lock来处理,使用了synchronized意味着内核态的一次切换。这是一个很重的操作。

有没有一种方式,可以比较便利的实现一些简单的数据同步,比如计数器等等。concurrent包下的atomic提供我们这么一种轻量级的数据同步的选择。

class MyThread implements Runnable {static AtomicInteger ai=new AtomicInteger(0);public void run() {for (int m = 0; m < 1000000; m++) {ai.getAndIncrement();}}
};public class TestAtomicInteger {public static void main(String[] args) throws InterruptedException {MyThread mt = new MyThread();Thread t1 = new Thread(mt);Thread t2 = new Thread(mt);t1.start();t2.start();Thread.sleep(500);System.out.println(MyThread.ai.get());}
}

在以上代码中,使用AtomicInteger声明了一个全局变量,并且在多线程中进行自增,代码中并没有进行显示的加锁。以上代码的输出结果,永远都是2000000。如果将AtomicInteger换成Integer,打印结果基本都是小于2000000。
      也就说明AtomicInteger声明的变量,在多线程场景中的自增操作是可以保证线程安全的。接下来我们分析下其原理。

原理

这里,我们来看看AtomicInteger是如何使用非阻塞算法来实现并发控制的。
      AtomicInteger的关键域只有一下3个:

public class AtomicInteger extends Number implements java.io.Serializable {private static final long serialVersionUID = 6214790243416807050L;// setup to use Unsafe.compareAndSwapInt for updatesprivate 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;/*** Creates a new AtomicInteger with the given initial value.** @param initialValue the initial value*/public AtomicInteger(int initialValue) {value = initialValue;}/*** Creates a new AtomicInteger with initial value {@code 0}.*/public AtomicInteger() {}......
}

这里, unsafe是java提供的获得对对象内存地址访问的类,注释已经清楚的写出了,它的作用就是在更新操作时提供“比较并替换”的作用。实际上就是AtomicInteger中的一个工具。

valueOffset是用来记录value本身在内存的编译地址的,这个记录,也主要是为了在更新操作在内存中找到value的位置,方便比较。

value是用来存储整数的时间变量,这里被声明为volatile。volatile只能保证这个变量的可见性。不能保证他的原子性。

可以看看getAndIncrement这个类似i++的函数,可以发现,是调用了UnSafe中的getAndAddInt。

    /*** Atomically increments by one the current value.** @return the previous value*/public final int getAndIncrement() {return unsafe.getAndAddInt(this, valueOffset, 1);}/*** Atomically sets to the given value and returns the old value.** @param newValue the new value* @return the previous value*/public final int getAndSet(int newValue) {return unsafe.getAndSetInt(this, valueOffset, newValue);}public final int getAndSetInt(Object var1, long var2, int var4) {int var5;do {var5 = this.getIntVolatile(var1, var2);//使用unsafe的native方法,实现高效的硬件级别CAS} while(!this.compareAndSwapInt(var1, var2, var5, var4));return var5;}

如何保证原子性:自旋 + CAS(乐观锁)。在这个过程中,通过compareAndSwapInt比较更新value值,如果更新失败,重新获取旧值,然后更新。

优缺点

CAS相对于其他锁,不会进行内核态操作,有着一些性能的提升。但同时引入自旋,当锁竞争较大的时候,自旋次数会增多。cpu资源会消耗很高。

换句话说,CAS+自旋适合使用在低并发有同步数据的应用场景。

Java 8做出的改进和努力

在Java 8中引入了4个新的计数器类型,LongAdder、LongAccumulator、DoubleAdder、DoubleAccumulator。他们都是继承于Striped64。

在LongAdder 与AtomicLong有什么区别?
Atomic*遇到的问题是,只能运用于低并发场景。因此LongAddr在这基础上引入了分段锁的概念。可以参考《JDK8系列之LongAdder解析》一起看看做了什么。

大概就是当竞争不激烈的时候,所有线程都是通过CAS对同一个变量(Base)进行修改,当竞争激烈的时候,会将根据当前线程哈希到对于Cell上进行修改(多段锁)。

    /*** Table of cells. When non-null, size is a power of 2.*/transient volatile Cell[] cells;/*** Base value, used mainly when there is no contention, but also as* a fallback during table initialization races. Updated via CAS.*/transient volatile long base;
/*** Padded variant of AtomicLong supporting only raw accesses plus CAS.** JVM intrinsics note: It would be possible to use a release-only* form of CAS here, if it were provided.*/@sun.misc.Contended static final class Cell {volatile long value;Cell(long x) { value = x; }final boolean cas(long cmp, long val) {return UNSAFE.compareAndSwapLong(this, valueOffset, cmp, val);}// Unsafe mechanicsprivate static final sun.misc.Unsafe UNSAFE;private static final long valueOffset;static {try {UNSAFE = sun.misc.Unsafe.getUnsafe();Class<?> ak = Cell.class;valueOffset = UNSAFE.objectFieldOffset(ak.getDeclaredField("value"));} catch (Exception e) {throw new Error(e);}}}

可以看到大概实现原理是:通过CAS乐观锁保证原子性,通过自旋保证当次修改的最终修改成功,通过降低锁粒度(多段锁)增加并发性能。

Refrence:
https://mp.weixin.qq.com/s/aw6OXC9wkxH42rCywNd7yQ

atomic的实现原理相关推荐

  1. Java并发编程包中atomic的实现原理

    转载自   Java并发编程包中atomic的实现原理 这是一篇来自粉丝的投稿,作者[林湾村龙猫]最近在阅读Java源码,这一篇是他关于并发包中atomic类的源码阅读的总结.Hollis做了一点点修 ...

  2. 【并发编程】Atomic的实现原理

    atomic原子类实现机制_atomic实现原理 [linux内核分析与应用-陈莉君]内核同步概述 1.直接操作内存,使用Unsafe这个类 2.使用 getIntVolatile(var1, var ...

  3. 1、Java中“并发编程”详解【voliate、synchronized、JMM内存模型、原子类操作Atomic..、CAS原理】

    文章目录 1.对volatile 的理解? JMM(Java 内存模型) JMM模型的三大特性: 禁止指令排序 线程安全性保证 你在哪些地方用到过 volatile?单例 2.CAS 你知道吗?CAS ...

  4. Java并发编程—Atomic原子类

    目录 Atomic 1. AtomicInteger a. 多线程并发访问问题 b. 用 AtomicInteger 类解决 2. AtomicIntegerArray a. 多线程并发访问问题 b. ...

  5. OS中atomic的实现解析

    OS中atomic的实现解析 转自:http://my.oschina.net/majiage/blog/267409  摘要 atomic属性线程安全,会增加一定开销,但有些时候必须自定义atomi ...

  6. atomic的安全性?

    之前的文章提到了,atomic保证了属性的原子性,但并不能保证线程的安全性,这种说法其实不是很准确. atomic保证了属性的原子性,并且在其有效范围内是线程安全的. 为什么这么说呢? 什么是原子性? ...

  7. java中atomic特点,Java中Atomic类的使用分析

    1:为什么会出现Atomic类 在多线程或者并发环境中,我们常常会遇到这种情况 int i=0; i++ 稍有经验的同学都知道这种写法是线程不安全的.为了达到线程安全的目的,我们通常会用synchro ...

  8. Lock的基本使用及原理笔记

    java.util.concurrent 并发工具包 Lock Lock是个接口,ReentrantLock唯一实现Lock的类 synchronized和ReentrantLock都支持重入锁 当多 ...

  9. Java之美[从菜鸟到高手演变]系列之博文阅读导航

    随着博文越来越多,为博客添加一个导航很有必要!本博客将相继开通Java.CloudFoundry.Linux.Ruby等专栏,都会设立目录,希望读者朋友们能更加方便的阅读! 在阅读的过程中有任何问题, ...

  10. MySql(16)——Spring data jpa mysql 乐观锁 与 AtomicInteger

    场景: 某对象被访问,并累计访问次数 特点: 1.表中该对象初始没有纪录 2.该对象首次被访问后,为其建立一条纪录 3.此后每次被访问,访问次数++ 4.该对象在表中有且仅有一条纪录 分析一下这个场景 ...

最新文章

  1. windows命令行下访问linux,Windows支持直接访问Linux子系统文件:你的下一台Linux何必是Linux...
  2. 持续交付流水线的敏捷利器:环境配置管理与应用部署自动化
  3. 网络号、主机号、子网号、子网掩码、子网划分
  4. bzoj1025 [SCOI2009]游戏 动态规划
  5. 东莞首办工业机器人成果交流会听众爆满
  6. kubelet启动失败_《蹲坑学kubernetes》之10-1:kubelet原理详解
  7. 2005链接mysql_VISUAL STUDIO 2005连接MYSQL数据库
  8. 开发人员不可不知的六大JavaScript框架 传统网站网页转移动端方式
  9. Godaddy 上的域名服务器状态查询
  10. Python Day10 MySQL 01
  11. 微信小程序--微信扫一扫登录pc页面功能的实现
  12. Hadoop安装教程_单机/伪分布式配置_Hadoop 2.7.7(2.7.7)/CentOS Linux release 7.4.1708
  13. 游戏音效的发展和制作游戏音效的意义
  14. AOSP ~ Camera - YUV格式简介
  15. JS实现省市县三级联动
  16. 云计算成就代码之美——首届阿里云开发者大赛巡礼
  17. Android带动画进度条简单实现
  18. 【已解决】打开游戏或者游戏引擎耳机出现滋滋滋的电流声
  19. 未来机器人生活的畅享之旅
  20. ISV 和SI 是什么

热门文章

  1. 生成式对抗网络(GAN)相关问题汇总(较全面)
  2. 微信小程序之店铺评分组件及vue中用svg实现的评分显示组件
  3. JS——正则校验域名
  4. 形容java工作者的句子_形容工作态度的句子
  5. 软件测试之因果图分析法
  6. python好找工作吗2017-2017年 Python工程师面试经历分享(七家)
  7. java-assured,如何使用Rest-Assured java中的证书进行HTTPS GET调用
  8. ASP.NET20003人事薪资管理系统
  9. L2十档行情API接口(十档委托、买卖队列、逐笔成交)web或软件应用xml数据接口
  10. android 智能识别名片,小程序云开发实战:实现 AI 智能名片识别小程序