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

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

cac55 2024-10-20 04:21 12 浏览 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学习小组”。

全栈工程师学习计划

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

版权声明

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

相关推荐

正点原子开拓者FPGA开发板资料连载第四十章 SD卡图片显示实验

1)实验平台:正点原子开拓者FPGA开发板2)摘自《开拓者FPGA开发指南》关注官方微信号公众号,获取更多资料:正点原子3)全套实验源码+手册+视频下载地址:http://www.openedv.c...

东芝存储改名为铠侠了,铠侠microSD卡128GB全网首测

作为一个数码爱好者,平时总爱把玩各种科技数码产品,最近又迷上了口袋云台相机,大疆OsmoPocket、飞宇口袋相机、SnoppaVmate口袋相机什么的,不过这类产品由于设计的机身体积很小(毕竟为...

SD存储卡卡面上奇奇怪怪的图标,你知道几个?

现在对高像素照片、连拍、4K甚至8K的需求越来越多,对存储卡的传输速度、容量等,要求也越来越多了。但是,看到SD存储卡卡面上奇奇怪怪的图标,让人非常迷惑。这篇文章让你简单认识这些图标和奇奇怪怪的数字。...

拍摄4K视频上选!铠侠 EXCERIA PLUS microSD卡

大家好,我是波导终结者。今天跟大家分享的是铠侠的EXCERIAPLUS极至光速microSDXCUHS-1存储卡,名字有点长,但是不用担心,我会帮大家梳理好存储卡的选购建议。有不少刚入门的朋友...

高速稳定,一卡多用:铠侠极至光速microSD存储卡评测

Hello,大家好,我是小胖子。半个月前收到了KIOXIA铠侠寄来的一张256GB的TF卡,用了大半个月,让我们看看这款产品表现如何吧。其实很多人并不太了解铠侠,问我铠侠是什么品牌,好不好。其实,东芝...

读速205MB/s、V30规格,雷克沙SILVER系列存储卡再添新成员

IT之家6月19日消息,雷克沙今日推出3款SILVER系列SD/microSD存储卡新品,支持4K60fps录像。据介绍,该系列存储卡均符合V30标准,其中micr...

相机、无人机拍视频,选择SD存储卡有什么需要知道的?

本文章不涉及产品推荐导购行为,致力于给到小白带来基础知识。相机一般使用SD卡,无人机一般使用microSD卡(也叫TF卡),使用的标准和图标标识是一样的。相机、无人机拍视频,选择SD存储卡有什么需要知...

PNY推出适用Switch 2的microSD Express卡,读取速度高达890MB/s

任天堂Switch2开始预订,其比前代产品变得更加昂贵,各种配件的价格都高于预期,这也包括转向microSDExpress存储。此时,PNY推出了新款microSDExpress闪存卡。新款mi...

SD卡迎来25周年:全球售出120亿张,容量翻50万倍

IT之家5月21日消息,科技媒体betanews今天(5月21日)发布博文,报道称SD卡迎来了25周年的生日。自2000年首款SD存储卡问世以来,已走过25个年头...

微单相机买一款什么样的SD卡才够用?写入速度更为关键

最近,评价君朋友发现自己的卡拍摄视频时候总断流,于是感觉写入速度应该是不够的,打算换卡,评价君正好跟他说道说道。目前的SD存储卡,很多只标注读取速度,比如95MB/s,80MB/s等等,而没有写写入速...

金士顿Canvas Go!Plus 系列存储卡评测

前言2020年,金士顿推出了CanvasGo!Plus系列存储卡,凭借其优秀的读写速度和稳定性获得了广大用户的认可。时隔5年,金士顿推出了其全新升级产品:SDG4/SDCG4,可选容量覆盖64GB...

TF卡速度等级|MK米客方德(tf卡速度等级图)

TF卡(TransFlash卡,又称MicroSD卡)是一种常见的便携式存储媒体,广泛用于智能手机、相机、平板电脑等设备中。TF卡的性能通常由速度等级来衡量,这些等级反映了TF卡的数据传输速度。拓优星...

关于SD卡,看这张表就够了(sd卡的作用)

这里是溢图科技(原“相机笔记”)。这两天有不少存储产品促销,随之而来的就是关于SD卡的一些提问。文章以前已经写过很多了,这里主要给大家看一张表格:上面就是SD卡协会官方制作的“族谱”,明确给出了不同版...

轻量化储存的首选——凯侠极致光速256G microSD存储卡实测

对于摄影师而言,我们经常会接触到相关存储设备,像照片拍摄中给相机安装的SD卡,视频录制中外录高规格画面的SSD等,都属于专业的存储介质,被应用于商业拍摄、电影级别拍摄之中。而针对生活中我们日常用于拍摄...

首发1569元,读取速度可达250MB/s,闪迪推出最新2TB至尊超极速存储卡

近日,闪迪(SanDisk)正式发布了其最新的2TB至尊超极速microSDXCUHS-I存储卡。据悉,这款存储卡的读取速度可达250MB/s,写入速度则达到150MB/s。这意味着用户在处理高分辨...

取消回复欢迎 发表评论: