网站首页 > 技术文章 正文
难度
初级
学习时间
30分钟
适合人群
零基础
开发语言
Java
开发环境
- JDK v11
- IntelliJIDEA v2018.3
友情提示
- 本教学属于系列教学,内容具有连贯性,本章使用到的内容之前教学中都有详细讲解。
- 本章内容针对零基础或基础较差的同学比较友好,可能对于有基础的同学来说很简单,希望大家可以根据自己的实际情况选择继续看完或等待看下一篇文章。谢谢大家的谅解!
1.温故知新
前面在《“全栈2019”53篇Java多线程学习资料及总结》一章中介绍了Java多线程基础知识总结及学习资料。
在《“全栈2019”Java原子操作第一章:内存可见性volatile关键字解析》一章中介绍了内存可见性volatile关键字。
在《“全栈2019”Java原子操作第二章:i++是原子操作吗?何为原子性》一章中介绍了什么是原子性。
在《“全栈2019”Java原子操作第三章:比较并交换CAS技术详解》一章中介绍了什么是比较并交换CAS技术。
在《“全栈2019”Java原子操作第四章:AtomicBoolean介绍与使用》一章中介绍了什么是原子操作类AtomicBoolean。
在《“全栈2019”Java原子操作第五章:AtomicInteger介绍与使用》一章中介绍了什么是原子操作类AtomicInteger。
现在介绍使用原子操作类AtomicInteger的方法实现更灵活的运算方式。
2.实现与任意数的加减乘除或更高级的运算
AtomicInteger类就不必多说了,在上一章中我们已经学过了基本知识。
之前学的方法中都只能自增或自减,还有就是与任意数的加法或减法。
比如像getAndIncrement()方法和incrementAndGet()方法是自增;
getAndDecrement()方法和decrementAndGet()方法是自减;
getAndAdd(int delta)方法和addAndGet(int delta)方法是和任意数相加或相减;
那我们要是想实现乘法、除法、平方和更高级的运算怎么做呢?
好像getAndUpdate(IntUnaryOperator updateFunction)方法和updateAndGet(IntUnaryOperator updateFunction)方法可以实现。
但这不是合理的做法。
我们先来试试getAndUpdate(IntUnaryOperator updateFunction)方法或updateAndGet(IntUnaryOperator updateFunction)方法怎么实现乘法(先实现高级运算中的一种)。
getAndUpdate(IntUnaryOperator updateFunction)方法和updateAndGet(IntUnaryOperator updateFunction)方法我们选择其中一个来实现就好了,这里选择updateAndGet(IntUnaryOperator updateFunction)方法。
updateAndGet(IntUnaryOperator updateFunction)方法的作用是先更新为指定值再返回当前值。
3.updateAndGet(IntUnaryOperator updateFunction)方法实现更乘法原子操作
首先,我们创建出AtomicInteger对象:
然后,调用updateAndGet(IntUnaryOperator updateFunction)方法:
接着,实现IntUnaryOperator接口:
这时候,我们可以在return处写上任意表达式,比如将当前值乘以3:
最后,我们输出结果:
例子书写完毕。
运行程序,执行结果:
从运行结果来看,符合预期。当前值为2,我们return的表达式是当前值乘以3,结果的确是6。
我们完成了当前值与任意值的乘法。
当然了,我们不想写一个固定值3在那里,我们想写的是一个变量。
改写例子。
创建一个变量x:
然后,将当前值与变量x相乘:
例子改写完毕。
运行程序,执行结果:
从运行结果来看,符合预期。当前值为2,我们return的表达式是当前值乘以变量值2,结果的确是6。
当前值与变量值的乘法也没有问题。
无论是固定值还是变量,以上的写法看似没有问题,却实际上有点不合理。
怎么不合理呢?
当匿名内部类访问外部变量时,外部变量必须是final类型的。也就是说变量x就是一个常量,和固定值没什么区别。如果我们能传入非final类型的变量就好了。
从代码上来看,变量x并没有被final关键字修饰啊:
这是因为当变量没有被修改时,final关键字可以省略不写,所以这里变量x没有被final关键字修饰。
怎么解决这个问题?
为此,AtomicInteger为我们提供了getAndAccumulate(int x,IntBinaryOperator accumulatorFunction)方法和accumulateAndGet(int x,IntBinaryOperator accumulatorFunction)方法,这两个方法可以使我们自定义运算方式。
究竟如何使用这两个方法,接下来我们来一起看看。
4.使用自定义运算方式更新当前值
getAndAccumulate(int x,IntBinaryOperator accumulatorFunction)方法在AtomicInteger类中的源码:
getAndAccumulate(int x,IntBinaryOperator accumulatorFunction)方法的作用是返回当前值再使用自定义运算方式更新当前值。
accumulateAndGet(int x,IntBinaryOperator accumulatorFunction)方法在AtomicInteger类中的源码:
accumulateAndGet(int x,IntBinaryOperator accumulatorFunction)方法的作用是先使用自定义运算方式更新当前值再返回当前值。
getAndAccumulate(int x,IntBinaryOperator accumulatorFunction)方法和accumulateAndGet(int x,IntBinaryOperator accumulatorFunction)方法中都需要一个IntBinaryOperator类型参数。
IntBinaryOperator是一个int类型的二元运算接口,它里面只有一个applyAsInt(int left, int right)方法:
applyAsInt(int left, int right)方法中有两个参数:left和right。
left代表左边的操作数;
right代表右边的操作数;
什么意思?
比如,x.applyAsInt(2,3),那么left = 2,right = 3。在AtomicInteger类中,left = 原值,rigth = 调用getAndAccumulate(int x,IntBinaryOperator accumulatorFunction)方法和accumulateAndGet(int x,IntBinaryOperator accumulatorFunction)方法中的参数int x。
接下来,我们来试试getAndAccumulate(int x,IntBinaryOperator accumulatorFunction)方法和accumulateAndGet(int x,IntBinaryOperator accumulatorFunction)方法。
新例子,不是上一小节的例子。
首先,创建出AtomicInteger对象并指定初始值,初始值为2:
然后,调用getAndAccumulate(int x,IntBinaryOperator accumulatorFunction)方法,在applyAsInt(int left, int right)方法中,我们要做乘法运算,也就是left*right:
接着,调用accumulateAndGet(int x,IntBinaryOperator accumulatorFunction)方法,同上面一样,在applyAsInt(int left, int right)方法中,我们也要做乘法运算,也就是left*right:
例子书写完毕。
运行程序,执行结果:
静图:
从运行结果来看,符合预期。
第一次运算时返回的是当前值,也就是还没运算过的原值,即2。
第一次做的是乘法运算,left = 2,right = 3,结果 = 6;
第二次运算时返回的是运算后的值,其中left = 6,right = 2;
第二次做的也是乘法运算,left * right = 6 * 2 = 12。
整个运算结果完全正确。
那么,使用getAndAccumulate(int x,IntBinaryOperator accumulatorFunction)方法或accumulateAndGet(int x,IntBinaryOperator accumulatorFunction)方法完成更加灵活的运算完全不在话下。
接下来,加减乘除就不演示了,演示一个平方运算。
5.AtomicInteger的平方运算
新例子,不是上一小节的例子。
首先,创建出AtomicInteger对象并指定初始值,初始值为2:
然后,我们调用accumulateAndGet(int x,IntBinaryOperator accumulatorFunction)方法,在applyAsInt(int left, int right)方法中,我们要做平方运算:
这里我们需要借助Math类的pow(double a, double b)方法。pow(double a, double b)方法的作用是实现a的b次方:
例子书写完毕。
运行程序,执行结果:
静图:
从运行结果来看,符合预期。2的3次方等于8,结果正确无误。
如果大家还有更多自定义的运算方式都可以使用getAndAccumulate(int x,IntBinaryOperator accumulatorFunction)方法和accumulateAndGet(int x,IntBinaryOperator accumulatorFunction)方法来实现,这样即保证了原子性也实现了预期操作。
最后,希望大家可以把这个例子照着写一遍,然后再自己默写一遍,方便以后碰到类似的面试题可以轻松应对。
祝大家编码愉快!
GitHub
本章程序GitHub地址:https://github.com/gorhaf/Java2019/tree/master/Thread/atomic/AtomicInteger
总结
- AtomicInteger是一个以原子方式操作int值的类。
- get()方法的作用是获取AtomicInteger对象的当前值;
- set?(int newValue)方法的作用是设置AtomicInteger对象的当前值。参数newValue是我们可以指定的新值。
- getAndIncrement()方法的作用是先返回当前值再递增。
- incrementAndGet()方法的作用是先递增再返回当前值。
- getAndDecrement()方法的作用是先返回当前值再递减。
- decrementAndGet()方法的作用是先递减再返回当前值。
- getAndAdd(int delta)方法的作用是先返回当前值再加上任意值。
- addAndGet(int delta)方法的作用是先加上任意值再返回当前值。
- getAndUpdate(IntUnaryOperator updateFunction)方法的作用是先返回当前值再更新为指定值。
- updateAndGet(IntUnaryOperator updateFunction)方法的作用是先更新为指定值再返回当前值。
- compareAndSet(int expectedValue, int newValue)方法的作用是如果当value==expectedValue,那么就将newValue赋给value,否则什么也不做。
- getAndAccumulate(int x,IntBinaryOperator accumulatorFunction)方法的作用是返回当前值再使用自定义运算方式更新当前值。
- accumulateAndGet(int x,IntBinaryOperator accumulatorFunction)方法的作用是先使用自定义运算方式更新当前值再返回当前值。
至此,Java中AtomicInteger相关内容讲解先告一段落,更多内容请持续关注。
答疑
如果大家有问题或想了解更多前沿技术,请在下方留言或评论,我会为大家解答。
上一章
“全栈2019”Java原子操作第五章:AtomicInteger介绍与使用
下一章
“全栈2019”Java原子操作第七章:AtomicLong介绍与使用
学习小组
加入同步学习小组,共同交流与进步。
- 方式一:关注头条号Gorhaf,私信“Java学习小组”。
- 方式二:关注公众号Gorhaf,回复“Java学习小组”。
全栈工程师学习计划
关注我们,加入“全栈工程师学习计划”。
版权声明
原创不易,未经允许不得转载!
猜你喜欢
- 2024-09-29 JAVA程序设计练习题一(附答案)(java程序设计题目题库)
- 2024-09-29 Java基础笔试练习(四)(java基础笔试题大全带答案)
- 2024-09-29 C/C++实战030:double转int常见的取整操作
- 2024-09-29 java-数据类型转换(Java数据类型转换规则)
- 2024-09-29 Java String字符串和整型int的相互转换
- 2024-09-29 Java教程:任意大整数的相加运算(java编程实现任意两个整数的大小)
- 2024-09-29 Java修炼终极指南:20-21. double和整数
- 2024-09-29 在java中String的方法:toString()怎么用
- 2024-09-29 Java基础之String与int两者之间如何相互转换?
- 2024-09-29 关于Java中的double类型数据(java中double类型占几个字节)
- 1509℃桌面软件开发新体验!用 Blazor Hybrid 打造简洁高效的视频处理工具
- 530℃Dify工具使用全场景:dify-sandbox沙盒的原理(源码篇·第2期)
- 493℃MySQL service启动脚本浅析(r12笔记第59天)
- 473℃服务器异常重启,导致mysql启动失败,问题解决过程记录
- 470℃启用MySQL查询缓存(mysql8.0查询缓存)
- 451℃「赵强老师」MySQL的闪回(赵强iso是哪个大学毕业的)
- 429℃mysql服务怎么启动和关闭?(mysql服务怎么启动和关闭)
- 426℃MySQL server PID file could not be found!失败
- 最近发表
- 标签列表
-
- c++中::是什么意思 (83)
- 标签用于 (65)
- 主键只能有一个吗 (66)
- c#console.writeline不显示 (75)
- pythoncase语句 (81)
- es6includes (73)
- windowsscripthost (67)
- apt-getinstall-y (86)
- node_modules怎么生成 (76)
- chromepost (65)
- c++int转char (75)
- static函数和普通函数 (76)
- el-date-picker开始日期早于结束日期 (70)
- js判断是否是json字符串 (67)
- checkout-b (67)
- localstorage.removeitem (74)
- vector线程安全吗 (70)
- & (66)
- java (73)
- js数组插入 (83)
- linux删除一个文件夹 (65)
- mac安装java (72)
- eacces (67)
- 查看mysql是否启动 (70)
- 无效的列索引 (74)