atomic包的原理及分析

JUC.atomic原子操作类原理分析 介绍java.util.concurrent.atomic包下类使用场景,着重介绍了Adder类分治思想实现高并发更新原理 阅读详情

Atomic简介

Atomic包是java.util.concurrent下的另一个专门为线程安全设计的Java包,包含多个原子操作类。这个包里面提供了一组原子变量类。其基本的特性就是在多线程环境下,当有多个线程同时执行这些类的实例包含的方法时,具有排他性,即当某个线程进入方法,执行其中的指令时,不会被其他线程打断,而别的线程就像自旋锁一样,一直等到该方法执行完成,才由JVM从等待队列中选择一个另一个线程进入,这只是一种逻辑上的理解。实际上是借助硬件的相关指令来实现的,不会阻塞线程(或者说只是在硬件级别上阻塞了)。可以对基本数据、数组中的基本数据、对类中的基本数据进行操作。原子变量类相当于一种泛化的volatile变量,能够支持原子的和有条件的读-改-写操作。

传统锁的问题

我们先来看一个例子:计数器(Counter),采用Java里比较方便的锁机制synchronized关键字,初步的代码如下:

class Counter {

private int value;

public synchronized int getValue() {

return value;

}

public synchronized int increment() {

return ++value;

}

public synchronized int decrement() {

return --value;

}

}

其实像这样的锁机制,满足基本的需求是没有问题的了,但是有的时候我们的需求并非这么简单,我们需要更有效,更加灵活的机制,synchronized关键字是基于阻塞的锁机制,也就是说当一个线程拥有锁的时候,访问同一资源的其它线程需要等待,直到该线程释放锁,这里会有些问题:首先,如果被阻塞的线程优先级很高很重要怎么办?其次,如果获得锁的线程一直不释放锁怎么办?(这种情况是非常糟糕的)。还有一种情况,如果有大量的线程来竞争资源,那CPU将会花费大量的时间和资源来处理这些竞争(事实上CPU的主要工作并非这些),同时,还有可能出现一些例如死锁之类的情况,最后,其实锁机制是一种比较粗糙,粒度比较大的机制,相对于像计数器这样的需求有点儿过于笨重,因此,对于这种需求我们期待一种更合适、更高效的线程安全机制。

硬件同步策略

现在的处理器都支持多重处理,当然也包含多个处理器共享外围设备和内存,同时,加强了指令集以支持一些多处理的特殊需求。特别是几乎所有的处理器都可以将其他处理器阻塞以便更新共享变量。

Compare and swap(CAS)

当前的处理器基本都支持CAS,只不过每个厂家所实现的算法并不一样罢了,每一个CAS操作过程都包含三个运算符:一个内存地址V,一个期望的值A和一个新值B,操作的时候如果这个地址上存放的值等于这个期望的值A,则将地址上的值赋为新值B,否则不做任何操作。CAS的基本思路就是,如果这个地址上的值和期望的值相等,则给其赋予新值,否则不做任何事儿,但是要返回原值是多少。我们来看一个例子,解释CAS的实现过程(并非真实的CAS实现):

class SimulatedCAS {

private int value;

public synchronized int getValue() {

return value;

}

public synchronized int compareAndSwap(int expectedValue, int newValue) {

int oldValue = value;

if (value == expectedValue)

value = newValue;

return oldValue;

}

}

下面是一个用CAS实现的Counter

public class CasCounter {

private SimulatedCAS value;

public int getValue() {

return value.getValue();

}

public int increment() {

int oldValue = value.getValue();

while (value.compareAndSwap(oldValue, oldValue + 1) != oldValue)

oldValue = value.getValue();

return oldValue + 1;

}

}

Atomic类

在JDK5.0之前,想要实现无锁无等待的算法是不可能的,除非用本地库,自从有了Atomic变量类后,这成为可能。下面这张图是包下的类结构。

 

标量类:AtomicBoolean,AtomicInteger,AtomicLong,AtomicReference

数组类:AtomicIntegerArray,AtomicLongArray,AtomicReferenceArray

更新器类:AtomicLongFieldUpdater,AtomicIntegerFieldUpdater,AtomicReferenceFieldUpdater

复合变量类:AtomicMarkableReference,AtomicStampedReference

第一组AtomicBoolean,AtomicInteger,AtomicLong,AtomicReference这四种基本类型用来处理布尔,整数,长整数,对象四种数据,其内部实现不是简单的使用synchronized,而是一个更为高效的方式CAS (compare and swap) + volatile和native方法,从而避免了synchronized的高开销,执行效率大为提升。我们来看个例子,与我们平时i++所对应的原子操作为:getAndIncrement()

public static void main(String[] args) {

AtomicInteger ai = new AtomicInteger();

System.out.println(ai);

ai.getAndIncrement();

System.out.println(ai);

}

我们可以看一下AtomicInteger的实现:

/**

* Atomically increments by one the current value.

*

* @return the previous value

*/

public final int getAndIncrement() {

return unsafe.getAndAddInt(this, valueOffset, 1);

}

这里直接调用一个叫Unsafe的类去处理,看来我们还需要继续看一下unsafe类的源码了。JDK8中sun.misc下UnSafe类,点击查看源码

从源码注释得知,这个类是用于执行低级别、不安全操作的方法集合。尽管这个类和所有的方法都是公开的(public),但是这个类的使用仍然受限,你无法在自己的java程序中直接使用该类,因为只有授信的代码才能获得该类的实例。所以我们平时的代码是无法使用这个类的,因为其设计的操作过于偏底层,如若操作不慎可能会带来很大的灾难,所以直接禁止普通代码的访问,当然JDK使用是没有问题的。

Atomic中的CAS

从前面的解释得知,CAS的原理是拿期望的值和原本的一个值作比较,如果相同则更新成新的值,此处这个“原本的一个值”怎么来,我们看看AtomicInteger里的实现:

// 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的一个方法objectFieldOffset(),查看源码:

/**

* Report the location of a given static field, in conjunction with {@link

* #staticFieldBase}.

* <p>Do not expect to perform any sort of arithmetic on this offset;

* it is just a cookie which is passed to the unsafe heap memory accessors.

*

* <p>Any given field will always have the same offset, and no two distinct

* fields of the same class will ever have the same offset.

*

* <p>As of 1.4.1, offsets for fields are represented as long values,

* although the Sun JVM does not use the most significant 32 bits.

* It is hard to imagine a JVM technology which needs more than

* a few bits to encode an offset within a non-array object,

* However, for consistency with other methods in this class,

* this method reports its result as a long value.

* @see #getInt(Object, long)

*/

public native long objectFieldOffset(Field f);

这个方法是用来拿到我们上文提到的这个“原来的值”的内存地址。是一个本地方法,返回值是valueOffset。它的参数field就是AtomicInteger里定义的value属性:

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() {

}

value是一个volatile变量,在内存中可见,任何线程都不允许对其进行拷贝,因此JVM可以保证任何时刻任何线程总能拿到该变量的最新值。此处value的值,可以在AtomicInteger类初始化的时候传入,也可以留空,留空则自动赋值为0。

我们再回到CAS,看看getAndIncrement()方法是怎么利用CAS实现的。

/**

* Atomically increments by one the current value.

*

* @return the previous value

*/

public final int getAndIncrement() {

return unsafe.getAndAddInt(this, valueOffset, 1);

}

继续:

public final int getAndAddInt(Object o, long offset, int delta) {

int v;

do {

v = getIntVolatile(o, offset);//------------0---------------

} while (!compareAndSwapInt(o, offset, v, v + delta));//-------------1-------------

return v;

}

/**

* Atomically update Java variable to <tt>x</tt> if it is currently

* holding <tt>expected</tt>.

* @return <tt>true</tt> if successful

*/

public final native boolean compareAndSwapInt(Object o, long offset,//---------------2--------------

int expected,

int x);

我稍微解释一下,其实compareAndSwapInt的注释解释的很明确,原子的将变量的值更新为x,如果成功了返回true,我们知道,如果我们创建AtomicInteger实例时不传入参数,则原始变量的值即为0,所以上面//----------0-----------处得到的v的值即为0,1处的代码为:

while(!compareAndSwapInt(o, offset, 0, 1))我们知道offset指向的地址对应的值就是原始变量的初值0,所以与期望的值0相同,所以将初值赋值为1,返回true,取反后为false,循环结束,返回v即更新之前的值0. 这就是类似于i++操作的原子操作的实现,当然最终CAS的实现都是native的,用C语言实现的,我们这里看不到源码,有时间我会反编译一下这段代码看看。

CAS线程安全

说了半天,我们要回归到最原始的问题了:这样怎么实现线程安全呢?请大家自己先考虑一下这个问题,其实我们在语言层面是没有做任何同步的操作的,大家也可以看到源码没有任何锁加在上面,可它为什么是线程安全的呢?这就是Atomic包下这些类的奥秘:语言层面不做处理,我们将其交给硬件—CPU和内存,利用CPU的多处理能力,实现硬件层面的阻塞,再加上volatile变量的特性即可实现基于原子操作的线程安全。所以说,CAS并不是无阻塞,只是阻塞并非在语言、线程方面,而是在硬件层面,所以无疑这样的操作会更快更高效!

总结

虽然基于CAS的线程安全机制很好很高效,但要说的是,并非所有线程安全都可以用这样的方法来实现,这只适合一些粒度比较小,型如计数器这样的需求用起来才有效,否则也不会有锁的存在了。

有问题请随时联系我,我会及时回复,欢迎一起探讨!

Java之美[从菜鸟到高手演练]之atomic包的原理及分析 Atomic包是java.util.concurrent下的另一个专门为线程安全设计的Java包,包含多个原子操作类。这个包里面提供了一组原子变量类。其基本的特性就是在多线程环境下,当有多个线程同时执行这些类的实例包含的方法时,具有排他性,即当某个线程进入方法,执行其中的指令时,不会被其他线程打断,而别的线程就像自旋锁一样,一直等到该方法执行完成,才由JVM从等待队列中选择一个另一个线程进入,这只是一种逻辑上的理解。实际上是借助硬件的相关指令来实现的,不会阻塞线程(或者说只是在硬件级别上阻塞了)。 阅读详情

相关推荐

说一下atomic 的原理?

atomic 通过CAS (Compare And Wwap)乐观锁机制-自旋锁(它的实现很简单,就是用一个预期的值和内存值进行比较,如果两个值相等,就用预期的值替换内存值,并返回 true。否则,返回 false。)保证原子性,【通过降低锁粒度(多段锁)增加并发性能。这一点是java8做出的改进】从而避免 synchronized 的高开销,执行效率大为提升。 ...

没有伞的孩子只能快跑,如若不跑,只有一个结果:超过你的人回头鄙视你 2813

JUC多线程:Atomic原子类与CAS原理

tomic 原子操作类是基于无锁 CAS + volatile 实现的,并且类中的所有方法都使用 final 修饰,进一步保证线程安全。而 CAS 算法的具体实现方式在于 Unsafe 类中,Unsafe 类的所有方法都是 native 修饰的,也就是说所有方法都是直接调用操作系统底层资源进行执行相应任务。Atomic 使用乐观策略,每次操作时都假设没有冲突发生,并采用 volatile 配合 CAS 去修改内存中的变量,如果失败则重试,直到成功为止。

张维鹏的博客 9966

MATPOWER的安装详细教程

MATPOWER的安装包实际为一个程序包,安装教程很简单,详见本文。

半个月亮Zz的博客 3万+

java.util.concurrent.atomic原子操作类

java.util.concurrent.atomic原子操作类java原子操作类1.原子操作类2.AtomicInteger的基本方法2.1 创建一个不传值的,默认值为02.2 获取和赋值2.3 compareAndSet方法2.4 getAndAdd、AddAndGet、getAndDecrement和DecrementAndGet3.多线程测试4.AtomicReference详解5.CAS可能存在ABA的问题5.1 AtomicStampedReference原理5.2 AtomicMarkable

不如打代码 1477

别再死记硬背了!用TwinCAT 3手把手教你配置EtherCAT伺服驱动的PDO映射(DS402协议)

本文详细介绍了如何使用TwinCAT 3配置EtherCAT伺服驱动的PDO映射(DS402协议),涵盖环境准备、设备扫描、PDO映射实战及高级技巧。通过具体操作步骤和常见问题排查,帮助工程师快速掌握EtherCAT通信配置,提升运动控制系统的效率和稳定性。

weixin_29185493的博客 324

Nvidia AGX Xavier刷机指北

因为工作的关系,要在AGX上安装CUDA。nvidia官网上没找到适合AGX版本的CUDA,只能走刷机的路子。刷机过程中,也遇到一些坑,做个记录。 需要有一台ubuntu系统的电脑作为主机Host。我的主机系统为Ubuntu18.04 整个刷机过程为: 在Host上安装sdkmanager,整个刷机过程主要通过sdkmanager来操作。 在Host自动下载一些系统组件,各种deb包 Host上制作OS系统镜像,烧录到AGX中 Host上之前下载好的deb包,安装到AGX中。 完结,撒花 ...

sinat_38685124的博客 6296

RT-Thread STM32F407 TCP客户端开发:从CubeMX到socket完整实现

嵌入式设备联网开发中,TCP/IP协议栈与实时操作系统的配合是构建稳定网络应用的基础。STM32F407凭借内置MAC和丰富外设,成为工业网关与物联网边缘设备的常见选择;而RT-Thread作为国产开源RTOS,通过sAL抽象层封装lwIP协议栈,为开发者提供了近乎标准的BSD socket接口,使网络编程门槛大幅降低。理解socket通信原理、掌握断线重连与数据收发机制,是开发可靠TCP客户端的关键。本文基于STM32F407 + LAN8720A硬件平台,结合RT-Thread的lwIP协议栈,从Cub

weixin_34195546的博客 336

LS-DYNA超高速碰撞仿真入门:从SPH算法配置到铝靶板穿孔分析(附K文件模板)

本文详细介绍了使用LS-DYNA中的SPH(光滑粒子流体动力学)算法进行超高速碰撞仿真的完整流程。以铝弹丸撞击铝靶板为例,从SPH算法原理、LS-PrePost前处理、材料模型与状态方程设置,到接触定义、边界条件及K文件生成,提供了系统的入门指导和关键参数调试经验,帮助工程师高效模拟材料破碎、穿孔等极端变形问题。

h0i1j2k3l的博客 735

Java中Atomic包的原理和分析

原文地址:http://blog.youkuaiyun.com/zhangerqing/article/details/43057799 Atomic简介 Atomic包是Java.util.concurrent下的另一个专门为线程安全设计的Java包,包含多个原子操作类。这个包里面提供了一组原子变量类。其基本的特性就是在多线程环境下,当有多个线程同时执行这些类的实例包含的方法时,具有排他性,

杨春建的博客 1万+

树莓派5新手必看:保姆级ibus中文输入法安装与配置全流程(含.bashrc设置避坑)

本文详细介绍了在树莓派5上安装和配置ibus中文输入法的完整流程,包括系统环境准备、ibus框架安装、图形界面配置及常见问题解决方案。通过清晰的步骤说明和避坑指南,帮助新手快速实现中文输入功能,提升开发效率。

weixin_29159441的博客 536

Dify工作流构建案例:迭代驱动的多维深度搜索

为了克服这些挑战,我们可以设计一个能够智能协同内部知识库与外部互联网搜索,并最终由大型语言模型(LLM)进行深度整合与精准回答的高级工作流。

同名公众号:dify实验室 2293

Atomic包的实现原理分析

Atomic简介 Atomic包是java.util.concurrent下的另一个专门为线程安全设计的Java包,包含多个原子操作类。这个包里面提供了一组原子变量类。其基本的特性就是在多线程环境下,当有多个线程同时执行这些类的实例包含的方法时,具有排他性,即当某个线程进入方法,执行其中的指令时,不会被其他线程打断,而别的线程就像自旋锁一样,一直等到该方法执行完成,才由JVM从等待队列中选择一个...

tyrroo的博客 316

【LLC快速上手系列】如何在LLC谐振里优雅地调死区时间?

用示波器死盯Vds波形,找到ZVS临界点;别听“经验值”,亲自调才准;死区时间并非越短越好,也不是越长越稳,而是要“刚刚好”;用热成像看MOSFET温升变化,ZVS调好了,温度马上掉2~5℃;轻载难ZVS?适当提升频率 or 降低Lr可破!

weixin_43199439的博客 497

java atomic包的原理及分析

Atomic简介 Atomic包是java.util.concurrent下的另一个专门为线程安全设计的Java包,包含多个原子操作类。这个包里面提供了一组原子变量类。其基本的特性就是在多线程环境下,当有多个线程同时执行这些类的实例包含的方法时,具有排他性,即当某个线程进入方法,执行其中的指令时,不会被其他线程打断,而别的线程就像自旋锁一样,一直等到该方法执行完成,才由JVM从等待队列中选择

shangruo的专栏 613

手把手教你学Simulink——基于全桥 LLC 谐振变换器的双向 DC-DC 建模与仿真

本文详细介绍了基于Simulink的双向全桥LLC谐振变换器建模与仿真方法。内容涵盖:1)LLC谐振变换器背景与双向化挑战;2)系统架构设计及关键数学推导(FHA增益分析、双向控制逻辑);3)Simulink建模步骤(功率级搭建、PWM生成、同步整流实现);4)仿真结果分析(正向/反向工作验证、ZVS特性);5)工程实践建议(增益微调、代码生成与HIL测试)。通过手把手教学,帮助读者掌握950V↔48V双向LLC变换器的完整设计流程,实现高效率能量双向流动的同时保持软开关特性,适用于储能系统、车载充电等应用

MHD0815的博客 179

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

Java并发之atomic包的原理及分析

Atomic包是java.util.concurrent下的另一个专门为线程安全设计的Java包,包含多个原子操作类。这个包里面提供了一组原子变量类。其基本的特性就是在多线程环境下,当有多个线程同时执行这些类的实例包含的方法时,具有排他性,即当某个线程进入方法,执行其中的指令时,不会被其他线程打断,而别的线程就像自旋锁一样,一直等到该方法执行完成,才由JVM从等待队列中选择一个另一个线程进入,这只是一种逻辑上的理解。

小文的博客 478

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

基于atomic包分析CAS原理

大家都知道,多线程下操作共享变量,会出现所谓的“线程安全问题”从而不能得到我们预期的结果,为了解决这种问题,在早期的JDK版本中,提供的synchronized关键字来解决这种线程安全问题,而在JDK1.5以后的java.util.concurrent包中,里面大量使用了一种叫CAS的技术,提供了一种不用synchronized的前提下解决线程安全问题的方案。本文将从AtomicInteger包...

三方 272

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

007-atomic包的原理及分析

一、Atomic简介   Atomic包是java.util.concurrent下的另一个专门为线程安全设计的Java包,包含多个原子操作类。这个包里面提供了一组原子变量类。其基本的特性就是在多线程环境下,当有多个线程同时执行这些类的实例包含的方法时,具有排他性,即当某个线程进入方法,执行其中的指令时,不会被其他线程打断,而别的线程就像自旋锁一样,一直等到该方法执行完成,才由JVM从等待队列中...

weixin_33978044的博客 116

Java原子性操作之——Atomic包的原理分析

Atomic: Atomic包是java.util.concurrent下的另一个专门为线程安全设计的java的包,包含多个原子性操作的类。基本特性就是在多线程情况下,当多个线程想要同时操作这些类的某些实例方法时,具有排他性,也就是当某个线程在执行某个方法时,不会被其他线程打断,其他线程会在外部等待,一直等到该方法执行完毕,才由JVM从等待队列中选择另一个线程进入,这只是一种逻辑上的理...

那些年的代码 223

并发编程-03线程安全性之原子性(Atomic包)及原理分析

文章目录脑图线程安全性的定义线程安全性的体现原子性使用AtomicInteger改造AtomicInteger#incrementAndGet分析CAS操作中可能会带来的ABA问题ABA问题的解决办法AtomicLong 和 LongAdderLongAdder的优化思路LongAdder的优缺点可见性有序性代码 脑图 线程安全性的定义 当多个线程访问某个类时,不管运行时环境采用何种调度方式或...

小工匠 2万+

java原子层_Java原子性操作之——Atomic包的原理分析

Atomic:Atomic包是java.util.concurrent下的另一个专门为线程安全设计的java的包,包含多个原子性操作的类。基本特性就是在多线程情况下,当多个线程想要同时操作这些类的某些实例方法时,具有排他性,也就是当某个线程在执行某个方法时,不会被其他线程打断,其他线程会在外部等待,一直等到该方法执行完毕,才由JVM从等待队列中选择另一个线程进入,这只是一种逻辑上的理解。实际上是借...

weixin_39805644的博客 203

010-多线程-JUC原子类-atomic包的原理及分析

一、Atomic简介   Atomic包是java.util.concurrent下的另一个专门为线程安全设计的Java包,包含多个原子操作类。这个包里面提供了一组原子变量类。其基本的特性就是在多线程环境下,当有多个线程同时执行这些类的实例包含的方法时,具有排他性,即当某个线程进入方法,执行其中的指令时,不会被其他线程打断,而别的线程就像自旋锁一样,一直等到该方法执行完成,才由JVM从...

dilv4062的博客 156
上一篇: Java中阻塞队列的使用
下一篇: JDK动态代理的实现及原理
java1856905
博客等级 码龄8年 100粉丝 249原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值