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

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

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

全栈工程师学习计划

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

版权声明

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

相关推荐

14款健身APP蹿红 看看下载最多的是哪款?

Zombies,Run!($3.99,安卓,iOS)如果你的运动理念是:除非有人追,否则绝不跑起来,那么这款APP应该适合你。Zombies,Run!这款程序把单调的跑步过程变身为躲避僵尸的游戏...

微软官方彩蛋庆祝《回到未来》纪念日

2015年10月21日,是MartyMcFly和Brown博士回到未来的时间。现在,这一天真的到了,那么当时影片中展示的一些科技产品究竟有多少实现了呢?作为一家走在技术前沿的公司,日前,微软就在M...

时尚圈最潮同志情侣 帅到没朋友(同志情侣微信头像)

来源:MSN时尚综合|2015-03-0419:45:15男演员ZacharyQuinto(中)与男模MilesMcMillan(右)于纽约街头公开热吻。情人节这个拥有不同起源传说,最早可以...

IE浏览器阻止过期ActiveX控件或将影响网银的使用

IE浏览器网银IE浏览器网银如果经常使用IE浏览器浏览网页的用户,可能都有遇到过浏览器窗口提示安装ActiveX控件的情况,一般情况下用户也是会选择直接安装。ActiveX控件广义上是指微软公司的整...

如何使Microsoft Band连接到WP设备

如果你幸运地购买到了MicrosoftBand,那么恭喜你。现在我们(winbeta)推出了“帮助系列”,那些尚未买到MicrosoftBand的朋友可以了解设备的一些新功能,以及设备的其他关键特...

毕业生不得不看的五大骗局全揭秘(毕业生防骗)

目前,距离高校大学生毕业已不足100天,大部分毕业生都十分忙碌。论文定稿、答辩,参加招聘、面试等成了应届毕业生的头等大事。但随着毕业季的临近,不法分子专门针对毕业生的诈骗高发期也随之来临。360手机安...

菠萝觅生活是O2O应用流量入口最大的供应商

现在主流的传统O2O生活服务,他们其实都有一个共通点,那就是各行其道。打车有快的,滴滴,外卖有饿了么,买机票有去哪儿网…每个APP都有着自己的核心竞争力。而用户呢?既想拥有海量有趣应用,又担心占用过多...

WP8.1版MSN健康应用,现已支持锁屏计步

IT之家(www.ithome.com):WP8.1版MSN健康应用,现已支持锁屏计步@WP之家报道,微软今天已将必应系列应用品牌归为MSN,除此之外,WP8.1版MSN健康和天气应用也获得一些新的...

短信就能传播手机病毒?看完推理惊呆了!

很多人都收到过一种带网址的陌生短信,有的人会点击网址看看,有的还会在好奇心驱使下回复短信。近日《北京新发现》栏目报道了一起离奇的电信诈骗案,事主耿先生的银行卡从未离身,但是在收到一条带网址的陌生短信,...

微软OneClip:我承包了你的剪贴板(微软onedrive云空间)

不久前,Twitter用户WalkingCat曝光了微软一款名为OneClip的应用。这是一款剪贴板应用,根据描述这款应用将覆盖Windows10(包括桌面和移动)、iOS和Android平台,可以...

Windows 10手机应该是什么样?微博用户给出了概念图

随着Windows10发布的不断临近,WindowsPhone的用户对Windows10的旗舰手机的期望也越来越高,我们WP中文网也在微博上发出了同样的问题,搜集用户对Windows10的硬...

云管家出席武汉2015年支付宝O2O生态峰会

2月4日,蚂蚁金服O2O生态峰会在武汉启幕。此次峰会展现了2015年蚂蚁金服在O2O领域的开放思路和策略,以及合作伙伴对O2O的创新观念及思路分享,吸引了武汉近3000名企业大佬、众多创业者、第三方服...

微软将于下周开启Windows开发中心帐号迁移工作

自下周开始微软将启动Windows开发中心的帐号迁移工作。根据WindowsBuildingApps博客透露Windows开发中心帐号迁移工作将会分为几个阶段。首个阶段从下周开始持续到今年7月份...

如何解绑已经合并的MSN账户和Skype账户?

如果您绑定的账户已经充值,建议您把产品消耗完毕后,再进行解绑。当您需要解绑合并的账户时,可登入Skype点卡账户自助操作。输入Skype或MSN账号、密码登录账户:登录后,可在页面左下角选择语言"中文...

微博账号已显示所属MCN机构,成为目前第二个上线该功能的平台

7月25日,多位网友发现,部分微博大V的个人主页已经显示其所属的MCN机构名称,微博也成为目前第二个上线该功能的平台。【来源:中新经纬】声明:此文版权归原作者所有,若有来源错误或者侵犯您的合法权益,您...

取消回复欢迎 发表评论: