难度
初级
学习时间
30分钟
适合人群
零基础
开发语言
Java
开发环境
- JDK v11
- IntelliJIDEA v2018.3
友情提示
- 本教学属于系列教学,内容具有连贯性,本章使用到的内容之前教学中都有详细讲解。
- 本章内容针对零基础或基础较差的同学比较友好,可能对于有基础的同学来说很简单,希望大家可以根据自己的实际情况选择继续看完或等待看下一篇文章。谢谢大家的谅解!
1.温故知新
前面在 一章中介绍了 什么是原子性 。
在 一章中介绍了 什么是比较并交换CAS技术 。
在 一章中介绍了 什么是原子操作类AtomicBoolean 。
在 一章中介绍了 什么是原子操作类AtomicInteger 。
在 一章中介绍了 使用原子操作类AtomicInteger的方法实现更灵活的运算方式 。
现在介绍 原子操作类Atomic long 。
2.什么是原子操作类?
顾名思义, 原子操作类就是实现了原子操作的类。
原子操作的概念和必备知识在该系列的 和 以及 三章中已经详细介绍过了,这里就不再赘述。不清楚的小伙伴请前去查阅相关章节。
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类里面几个常用方法及源码。
对于学过 一章的小伙伴来说,掌握下面的内容轻而易举,因为变化的仅仅是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算法体现
我们之前在 一章中学习过什么是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地址:
总结
- 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相关内容讲解先告一段落,更多内容请持续关注。
答疑
如果大家有问题或想了解更多前沿技术,请在下方留言或评论,我会为大家解答。
上一章
下一章
学习小组
加入同步学习小组,共同交流与进步。
- 方式一:关注头条号Gorhaf,私信“Java学习小组”。
- 方式二:关注公众号Gorhaf,回复“Java学习小组”。
全栈工程师学习计划
关注我们,加入“全栈工程师学习计划”。
版权声明
原创不易,未经允许不得转载!