百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

“全栈2019”Java原子操作第七章:AtomicLong介绍与使用

cac55 2024-10-20 04:21 15 浏览 0 评论

难度

初级

学习时间

30分钟

适合人群

零基础

开发语言

Java

开发环境

  • JDK v11
  • IntelliJIDEA v2018.3

友情提示

  • 本教学属于系列教学,内容具有连贯性,本章使用到的内容之前教学中都有详细讲解。
  • 本章内容针对零基础或基础较差的同学比较友好,可能对于有基础的同学来说很简单,希望大家可以根据自己的实际情况选择继续看完或等待看下一篇文章。谢谢大家的谅解!

1.温故知新

前面在《“全栈2019”Java原子操作第二章:i++是原子操作吗?何为原子性》一章中介绍了什么是原子性

《“全栈2019”Java原子操作第三章:比较并交换CAS技术详解》一章中介绍了什么是比较并交换CAS技术

《“全栈2019”Java原子操作第四章:AtomicBoolean介绍与使用》一章中介绍了什么是原子操作类AtomicBoolean

《“全栈2019”Java原子操作第五章:AtomicInteger介绍与使用》一章中介绍了什么是原子操作类AtomicInteger

《“全栈2019”Java原子操作第六章:AtomicInteger灵活的运算方式》一章中介绍了使用原子操作类AtomicInteger的方法实现更灵活的运算方式

现在介绍原子操作类AtomicLong

2.什么是原子操作类?

顾名思义,原子操作类就是实现了原子操作的类。

原子操作的概念和必备知识在该系列的《“全栈2019”Java原子操作第一章:内存可见性volatile关键字解析》《“全栈2019”Java原子操作第二章:i++是原子操作吗?何为原子性》以及《“全栈2019”Java原子操作第三章:比较并交换CAS技术详解》三章中已经详细介绍过了,这里就不再赘述。不清楚的小伙伴请前去查阅相关章节。

2.AtomicLong简介

AtomicLong是一个以原子方式操作long值的类。

先简单来看一下AtomicLong类长什么样:

这里需要注意的是,AtomicLong和AtomicInteger类一样继承了Number类,说明它不能替代Long类,只是扩展了Number类。

AtomicLong类很简单,它有两个构造方法:

如下:

  • AtomicLong()
  • AtomicLong?(long initialValue)

这两个构造方法都比较常用。

第二个构造方法AtomicLong?(long initialValue)可以指定初始值。

下面我们就来用一用AtomicLong。

3.AtomicLong应用场景

例如,我们某些应用需要生成序列号或唯一ID时,那么就可以用到AtomicLong。

如果大家有更多关于AtomicLong的应用场景请在评论区留言,谢谢。

下面,来看看未使用AtomicLong的时候会是怎样。

例子是序列号生成器。

首先,我们创建一个序列号生成器类:

然后,将其构造方法进行私有化,目的是不让创建对象,只能使用其静态方法:

接着,定义一个long类型的变量记录当前生成的序列号:

然后,定义一个获取当前序列号的方法:

接着,再定义一个生成序列号的方法:

序列号生成器类写好了。

接下来,我们去试试序列号生成器。

在Main类中创建一个生成序列号任务:

接着,在run方法里面调用生成器类对象的生成序列号的方法并输出结果:

run()方法书写完毕。

然后,我们创建100个线程去生成序列号:

最后,等这100个线程都执行完生成序列号任务以后,我们再次生成序列号:

如果当前生成的序列号是100,则说明每个线程都有自己唯一的序列号,都是不同的;

如果当前生成的序列号不是100,则说明有序列号相同的线程,序列号生成器这个类就有问题。

不过在此之前需要睡1秒钟,目的是等这100个线程执行完毕之后我们再生成:

例子书写完毕。

运行程序,执行结果:

从运行结果来看,符合预期。最后生成的序列号的确为100,看似生成序列号这个类没有问题。

生成序列号这个类真的没有问题吗?

我们修改例子再试试。

在生成序列号任务中,使执行生成序列号方法之前睡100毫秒:

例子改写完毕。

运行程序,执行结果:

从运行结果来看,符合预期。当前序列号不为100,说明生成序列号这个类是有问题的。

怎么解决?

这时我们的AtomicLong类派上用场了。

我们只需用AtomicLong替换long类型即可:

因为AtomicLong替换了long,所以获取序列号方法和生成序列号方法都得改变。

先是获取当前序列号方法:

然后是生成序列号方法:

对于学过前面几章的小伙伴来说,get()方法和getAndIncrement()方法不陌生,get()方法是用来获取sequenceNumber的当前值;getAndIncrement()方法是用来递增sequenceNumber对象值的,相当于i++。

例子改写完毕。

运行程序,执行结果:

从运行结果来看,符合预期。当前序列号为100,生成序列号这个类之前的问题得到了解决。

至此,序列号生成器的问题就先告一段落了。

下面就开始说说AtomicLong类里面几个常用方法及源码。

对于学过《“全栈2019”Java原子操作第五章:AtomicInteger介绍与使用》一章的小伙伴来说,掌握下面的内容轻而易举,因为变化的仅仅是int类型变为long类型。

4.获取get()/设置set?(long newValue)当前值方法

先来看看AtomicLong类这两个方法:

  • get()
  • set?(long newValue)

下面依次来看看这两个方法。

get()方法在AtomicLong类中的源码:

get()方法的作用是获取AtomicLong对象的当前值;

set?(long newValue)方法在AtomicLong类中的源码:

set?(long newValue)方法的作用是设置AtomicLong对象的当前值。参数newValue是我们可以指定的新值。

接下来,我们来试试get()方法和set?(long newValue)方法。

新例子,不是上一小节的例子。

首先,创建出AtomicLong对象并指定初始值:

然后,调用value的get()方法获取当前值:

接着,调用value的set?(long newValue)方法指定新值:

最后,调用value的get()方法再次获取当前值,看新值是否被设置成功:

例子书写完毕。

运行程序,执行结果:

从运行结果来看,符合预期。值从0变化为1。

5.递增方法

AtomicLong类中有两个递增方法:

  • getAndIncrement()
  • incrementAndGet()

其中,getAndIncrement()方法相当于i++,incrementAndGet()方法相当于++i。

我们来依次看看这两个方法。

getAndIncrement()方法在AtomicLong类中的源码:

getAndIncrement()方法的作用是返回递增前的值。

incrementAndGet()方法在AtomicLong类中的源码:

incrementAndGet()方法的作用是返回递增后的值。

接下来,我们来试试getAndIncrement()方法和incrementAndGet()方法。

新例子,不是上一小节的例子。

首先,我们创建一个AtomicLong对象并指定初始值:

然后,调用getAndIncrement()方法:

最后,调用incrementAndGet()方法:

例子书写完毕。

运行程序,执行结果:

从运行结果来看,符合预期。

首先调用getAndIncrement()方法时,value的值为0,根据getAndIncrement()方法的先返回当前值再递增的原则,第一次输出结果正确;

getAndIncrement()方法递增完之后,value的值为1;

接下来,调用incrementAndGet()方法时,value值为1,根据incrementAndGet()方法的先递增再返回当前值的原则,第二次输出结果也正确;

incrementAndGet()方法先递增的结果就是:1被递增之后变为2。

说完AtomicLong类中的递增方法,再来说说AtomicLong类中的递减方法。

6.递减方法

AtomicLong类中有两个递减方法:

  • getAndDecrement()
  • decrementAndGet()

其中,getAndDecrement()方法相当于i--,decrementAndGet()方法相当于--i。

我们来依次看看这两个方法。

getAndDecrement()方法在AtomicLong类中的源码:

getAndDecrement()方法的作用是返回递减前的值。

decrementAndGet()方法在AtomicLong类中的源码:

decrementAndGet()方法的作用是返回递减后的值。

接下来,我们来试试getAndDecrement()方法和decrementAndGet()方法。

新例子,不是上一小节的例子。

首先,我们创建一个AtomicLong对象并指定初始值:

然后,调用getAndDecrement()方法:

最后,调用decrementAndGet()方法:

例子书写完毕。

运行程序,执行结果:

从运行结果来看,符合预期。

首先调用getAndDecrement()方法时,value的值为0,根据getAndDecrement()方法的先返回当前值再递减的原则,第一次输出结果正确;

getAndDecrement()方法递减完之后,value的值为-1;

接下来,调用decrementAndGet()方法时,value值为-1,根据decrementAndGet()方法的先递减再返回当前值的原则,第二次输出结果也正确;

decrementAndGet()方法先递减的结果就是:-1被递减之后结果为-2。

7.加上任意值方法

前面两小节演示的是自增和自减情况,如果想要加上任意数,比如+5,或者你想要减去任意数,比如-5,使用前面那几个方法是做不到的,所以AtomicLong类也为我们提供相应方法。

AtomicLong类中有两个加上任意值的方法:

  • getAndAdd(long delta)
  • addAndGet(long delta)

如果你是做加法,那么就直接写一个正数即可;如果你是想做减法,那么就直接写一个负数即可。

我们来依次看看这两个方法。

getAndAdd(long delta)方法在AtomicLong类中的源码:

getAndAdd(long delta)方法的作用是返回加上任意值前的值。

addAndGet(long delta)方法在AtomicLong类中的源码:

addAndGet(long delta)方法的作用是返回加上任意值后的值。

接下来,我们来试试getAndAdd(long delta)方法和addAndGet(long delta)方法。

新例子,不是上一小节的例子。

首先,我们创建一个AtomicLong对象并指定初始值:

然后,调用getAndAdd(long delta)方法:

最后,调用addAndGet(long delta)方法:

例子书写完毕。

运行程序,执行结果:

从运行结果来看,符合预期。

首先调用getAndAdd(long delta)方法时,value的值为0,根据getAndAdd(long delta)方法的先返回当前值再加上任意值的原则,第一次输出结果正确;

getAndAdd(long delta)方法加完任意值之后,value的值为3;

接下来,调用addAndGet(long delta)方法时,value值为3,根据addAndGet(long delta)方法的先加上任意值再返回当前值的原则,第二次输出结果也正确;

addAndGet(long delta)方法先加上任意值的结果就是:3加上-1结果为2。

8.自定义更新方式

前面几个小节演示的都是和指定数的加法或减法,如果我想自定义更新value的值怎么办呢?

AtomicLong类为我们提供了两个可以自定义更新value值的方法:

  • getAndUpdate(LongUnaryOperator updateFunction)
  • updateAndGet(LongUnaryOperator updateFunction)

怎么更新呢?

你只需传入LongUnaryOperator接口的实现即可。

我们先来看看LongUnaryOperator接口:

LongUnaryOperator接口里面我们只需实现applyAsLong(long operand)方法即可

applyAsLong(long operand)方法中的值是我们调用getAndUpdate(LongUnaryOperator updateFunction)方法或updateAndGet(LongUnaryOperator updateFunction)方法时传递过去的

下面,我们来依次看看这两个方法。

getAndUpdate(LongUnaryOperator updateFunction)方法在AtomicLong类中的源码:

getAndUpdate(LongUnaryOperator updateFunction)方法的作用是返回更新前的值。

updateAndGet(LongUnaryOperator updateFunction)方法在AtomicLong类中的源码:

updateAndGet(LongUnaryOperator updateFunction)方法的作用是返回更新后的值。

接下来,我们来试试getAndUpdate(LongUnaryOperator updateFunction)方法和updateAndGet(LongUnaryOperator updateFunction)方法。

新例子,不是上一小节的例子。

首先,我们创建一个AtomicLong对象并指定初始值:

然后,调用getAndUpdate(LongUnaryOperator updateFunction)方法:

最后,调用updateAndGet(LongUnaryOperator updateFunction)方法:

例子书写完毕。

运行程序,执行结果:

从运行结果来看,符合预期。

首先调用getAndUpdate(LongUnaryOperator updateFunction)方法时,value的值为0,根据getAndUpdate(LongUnaryOperator updateFunction)方法的先返回当前值再更新为指定值的原则,第一次输出结果正确;

getAndUpdate(LongUnaryOperator updateFunction)方法更新为指定值之后,value的值为1,即 0 + 1 = 1;

接下来,调用updateAndGet(LongUnaryOperator updateFunction)方法时,value值为1,根据updateAndGet(LongUnaryOperator updateFunction)方法的先更新为指定值再返回当前值的原则,第二次输出结果也正确;

updateAndGet(LongUnaryOperator updateFunction)方法先更新为指定值的结果就是:1 * 2 = 2。

9.使用自定义运算方式更新当前值

如果我们想用AtomicLong对象以原子的方式做更高级更灵活的更新方式,那么最好是使用getAndAccumulate(long x,LongBinaryOperator accumulatorFunction)方法和accumulateAndGet(long x,LongBinaryOperator accumulatorFunction)方法。

下面,我们来依次看看这两个方法。

getAndAccumulate(long x,LongBinaryOperator accumulatorFunction)方法在AtomicLong类中的源码:

getAndAccumulate(long x,LongBinaryOperator accumulatorFunction)方法的作用是返回使用自定义运算方式更新前的值。

accumulateAndGet(long x,LongBinaryOperator accumulatorFunction)方法在AtomicLong类中的源码:

accumulateAndGet(long x,LongBinaryOperator accumulatorFunction)方法的作用是返回使用自定义运算方式更新后的值。

getAndAccumulate(long x,LongBinaryOperator accumulatorFunction)方法和accumulateAndGet(long x,LongBinaryOperator accumulatorFunction)方法中都需要一个LongBinaryOperator类型参数。

LongBinaryOperator是一个long类型的二元运算接口,它里面只有一个applyAsLong(long left, long right)方法:

applyAsLong(long left, long right)方法中有两个参数:left和right。

left代表左边的操作数;

right代表右边的操作数;

什么意思?

比如,x.applyAsLong(2,3),那么left = 2,right = 3。在AtomicLong类中,left = 原值,rigth = 调用getAndAccumulate(long x,LongBinaryOperator accumulatorFunction)方法和accumulateAndGet(long x,LongBinaryOperator accumulatorFunction)方法中的参数long x。

接下来,我们来试试getAndAccumulate(long x,LongBinaryOperator accumulatorFunction)方法和accumulateAndGet(long x,LongBinaryOperator accumulatorFunction)方法。

新例子,不是上一小节的例子。

首先,创建出AtomicLong对象并指定初始值,初始值为2:

然后,调用getAndAccumulate(long x,LongBinaryOperator accumulatorFunction)方法,在applyAsLong(long left, long right)方法中,我们要做乘法运算,也就是left*right:

接着,调用accumulateAndGet(long x,LongBinaryOperator accumulatorFunction)方法,同上面一样,在applyAsLong(long left, long right)方法中,我们也要做乘法运算,也就是left*right:

例子书写完毕。

运行程序,执行结果:

从运行结果来看,符合预期。

第一次运算时返回的是当前值,也就是还没运算过的原值,即2。

第一次做的是乘法运算,left = 2,right = 3,结果 = 6;

第二次运算时返回的是运算后的值,其中left = 6,right = 2;

第二次做的也是乘法运算,left * right = 6 * 2 = 12。

整个运算结果完全正确。

那么,使用getAndAccumulate(long x,LongBinaryOperator accumulatorFunction)方法和accumulateAndGet(long x,LongBinaryOperator accumulatorFunction)方法完成更加灵活的运算完全不在话下。

10.CAS算法体现

我们之前在《“全栈2019”Java原子操作第三章:比较并交换CAS技术详解》一章中学习过什么是CAS算法。

在AtomicLong类中也有体现:

compareAndSet(long expectedValue, long newValue)方法的作用是如果当value==expectedValue,那么就将newValue赋给value,否则什么也不做。

下面我们来试试compareAndSet(long expectedValue, long newValue)方法。

新例子,不是上一小节的例子。

首先,我们创建一个AtomicLong对象并指定初始值,初始值为0:

然后,在调用compareAndSet(long expectedValue, long newValue)方法之前获取一次value的值:

接着,调用compareAndSet(long expectedValue, long newValue)方法并输出方法返回结果:

最后,我们再获取一次value的值看发生变化没有:

例子改写完毕。

运行程序,执行结果:

从运行结果来看,符合预期。

当然了,你也可以将预期值改为一个非原值的数,这样赋值就不成功。

最后,希望大家可以把这个例子照着写一遍,然后再自己默写一遍,方便以后碰到类似的面试题可以轻松应对。

祝大家编码愉快!

GitHub

本章程序GitHub地址:https://github.com/gorhaf/Java2019/tree/master/Thread/atomic/AtomicLong

总结

  • AtomicLong是一个以原子方式操作long值的类。
  • get()方法的作用是获取AtomicLong对象的当前值;
  • set?(long newValue)方法的作用是设置AtomicLong对象的当前值。参数newValue是我们可以指定的新值。
  • getAndIncrement()方法的作用是返回递增前的值。
  • incrementAndGet()方法的作用是返回递增后的值。
  • getAndDecrement()方法的作用是返回递减前的值。
  • decrementAndGet()方法的作用是返回递减后的值。
  • getAndAdd(long delta)方法的作用是返回加上任意值前的值。
  • addAndGet(long delta)方法的作用是返回加上任意值后的值。
  • getAndUpdate(LongUnaryOperator updateFunction)方法的作用是返回更新前的值。
  • updateAndGet(LongUnaryOperator updateFunction)方法的作用是返回更新后的值。
  • getAndAccumulate(long x,LongBinaryOperator accumulatorFunction)方法的作用是返回使用自定义运算方式更新前的值。
  • accumulateAndGet(long x,LongBinaryOperator accumulatorFunction)方法的作用是返回使用自定义运算方式更新后的值。
  • compareAndSet(long expectedValue, long newValue)方法的作用是如果当value==expectedValue,那么就将newValue赋给value,否则什么也不做。

至此,Java中AtomicLong相关内容讲解先告一段落,更多内容请持续关注。

答疑

如果大家有问题或想了解更多前沿技术,请在下方留言或评论,我会为大家解答。

上一章

“全栈2019”Java原子操作第六章:AtomicInteger灵活的运算方式

下一章

“全栈2019”Java原子操作第八章:AtomicReference介绍与使用

学习小组

加入同步学习小组,共同交流与进步。

  • 方式一:关注头条号Gorhaf,私信“Java学习小组”。
  • 方式二:关注公众号Gorhaf,回复“Java学习小组”。

全栈工程师学习计划

关注我们,加入“全栈工程师学习计划”。

版权声明

原创不易,未经允许不得转载!

相关推荐

MIRIX重塑AI记忆:超Gemini 410%,节省99.9%内存,APP同步上线

MIRIX,一个由UCSD和NYU团队主导的新系统,正在重新定义AI的记忆格局。在过去的十年里,我们见证了大型语言模型席卷全球,从写作助手到代码生成器,无所不能。然而,即使最强大的模型依...

硬盘坏了怎么把数据弄出来对比10种硬盘数据恢复软件

机械硬盘或固态硬盘损坏导致数据丢失时,应立即停止对硬盘的读写操作,并根据损坏类型选择逻辑层恢复工具或专业物理恢复服务。紧急处置措施立即停止通电使用:发现硬盘异响、无法识别或数据异常时,需立即断开连接,...

蓝宝石B850A WIFI主板新玩法:内存小参调节体验

蓝宝石前段时间发布了一款性价比极高的主板:NITRO氮动B850AWIFI主板。这款主板的售价只要1349元,相比普遍1500元以上的B850主板,确实极具竞争力。虽然价格实惠,蓝宝石NITR...

内存卡损坏读不出怎么修复?这5个数据恢复工具汇总,3秒挽回!

在数字化生活的浪潮中,内存卡凭借小巧便携与大容量存储的特性,成为相机、手机、行车记录仪等设备存储数据的得力助手,承载着无数珍贵回忆与重要文件。然而,当内存卡突然损坏无法读取,无论是误删、格式化、病毒入...

内存卡修复不再难,2025年必学的6款软件工具

内存卡出现问题时,通常是因为文件系统损坏、物理损坏或病毒感染。通过专业的修复工具,我们可以尝试恢复数据并修复内存卡。内存卡修复利器:万兴恢复专家万兴恢复专家是一款功能强大的数据恢复软件,支持多种设备和...

有5款内存卡修复工具汇总,内存卡数据轻松找回!

在如今的数字时代,内存卡作为不可或缺的存储介质,广泛应用于相机、手机、行车记录仪等各类设备中,承载着我们珍贵的照片、视频以及重要文件。然而,数据丢失的风险却如影随形,误删、格式化、病毒入侵、硬件故障等...

揭秘:如何通过多种方式精准查询内存条型号及规避风险?

以下是内存条型号查询的常用方法及注意事项,综合了物理查看、软件检测、编码解析等多种方式:一、物理标签查看法1.拆机查看标签打开电脑主机/笔记本后盖找到内存条,观察标签上的型号标识。例如内存标签通常标...

内存卡数据恢复5个工具汇总推荐,轻松找回珍贵记忆!

在这个数字化时代,内存卡作为我们存储珍贵照片、重要文件的常用载体,广泛应用于手机、相机、平板电脑等设备。但数据丢失的意外却常常不期而至,误删除、格式化、病毒攻击,甚至内存卡的物理损坏,都可能让辛苦保存...

电脑内存智能监控清理,优化性能的实用软件

软件介绍Memorycleaner是一款内存清理软件。功能很强,效果很不错。Memorycleaner会在内存用量超出80%时,自动执行“裁剪进程工作集”“清理系统缓存”以及“用全部可能的方法清理...

TechPowerUp MemTest64:内存稳定性测试利器

TechPowerUpMemTest64:内存稳定性测试利器一、软件简介TechPowerUpMemTest64,由知名硬件信息工具GPU-Z的出品公司TechPowerUp发布,是一款专为64位...

微软推出AI恶意软件检测智能体Project Ire,精确度高达98%

IT之家8月6日消息,当地时间周二,微软宣布推出可自主分析恶意软件的AI检测系统原型——ProjectIre。该项目由微软研究院、Defender研究团队及Discovery&a...

农村老木匠常用的20种老工具,手艺人靠它养活一家人,你认识几种

生活中的手艺老匠人是非常受到尊敬和崇拜的,特别是在农村曾经的老匠人都是家里的“座上宾”。对于民间传统的手艺人,有一种说法就是传统的八大匠:木匠、泥匠、篾匠、铁匠、船匠、石匠、油匠和剃头匠。木匠的祖始爷...

恶意木马新变种伪装成聊天工具诱人点击

国家计算机病毒应急处理中心通过对互联网监测发现,近期出现一种恶意木马程序变种Trojan_FakeQQ.CTU。该变种通过伪装成即时聊天工具,诱使计算机用户点击运行。该变种运行后,将其自身复制到受感染...

学习网络安全 这些工具你知道吗?

工欲善其事必先利其器,在新入门网络安全的小伙伴而言。这些工具你必须要有所了解。本文我们简单说说这些网络安全工具吧!Web安全类web类工具主要是通过各种扫描工具,发现web站点存在的各种漏洞...

5分钟盗走你的隐私照片,这个全球性漏洞到底有多可怕?

这个时代,大家对电脑出现漏洞,可能已经习以为常。但如果机哥告诉大家,这个漏洞能够在5分钟内,破解并盗取你所有加密文件,而且还无法通过软件和补丁修复...这可就有点吓人啦。事情是酱婶的。来自荷兰埃因...

取消回复欢迎 发表评论: