首页 / JAVA / Java原子类中CAS详解
Java原子类中CAS详解
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了Java原子类中CAS详解,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含4740字,纯文字阅读大概需要7分钟。
内容图文
![Java原子类中CAS详解](/upload/InfoBanner/zyjiaocheng/607/e341261fadb5453195120c071ef7cc9b.jpg)
Java原子类中CAS详解
在Java中使用volatile关键字不保证操作的原子性从而在多线程环境下会出现问题,解决方法可以使用琐机制使用synchronized和lock进行加锁但是效率极低一般不使用这种方式解决原子性问题,在Java中的java.util.concurrent.atomic 包下有各种数据类型的原子类,使用原子类型来解决原子性问题最为高效
public class Atomicity {
private static int count = 0;
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(()->{
for (int j = 0; j < 1000; j++) {
count++;
}
}).start();
}
//Java中至少有两个线程 main 线程和 gc 线程
while(Thread.activeCount()>2){
Thread.yield();
}
System.out.println(count);
}
}
测试结果:
很显然没有使用原子类出现了原子性问题
使用原子类
public class Atomicity {
private static AtomicInteger count = new AtomicInteger(0);
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(()->{
for (int j = 0; j < 1000; j++) {
count.incrementAndGet();
}
}).start();
}
//Java中至少有两个线程 main 线程和 gc 线程
while(Thread.activeCount()>2){
Thread.yield();
}
System.out.println(count);
}
}
测试结果:
没有出现原子性问题
原子类保证原子性是因为底层使用CAS机制,根本原因就是硬件底层执行了一条cpu指令仅凭该指令可以完成"比较并交换"一系列操作
compare and swap,解决多线程并行情况下使用锁造成性能损耗的一种机制,CAS操作包含三个操作数——内存位置(V)、预期原值(A)和新值(B)。如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值。否则,处理器不做任何操作。无论哪种情况,它都会在CAS指令之前返回该位置的值。CAS有效地说明了“我认为位置V应该包含值A;如果包含该值,则将B放到这个位置;否则,不要更改该位置,只告诉我这个位置现在的值即可。 ——《百度百科》
incrementAndGet()方法源码:
/**
* Atomically increments by one the current value.
*
* @return the updated value
*/
public final int incrementAndGet() {
return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}
从源码可以看出,方法内又使用了unsafe对象的getAndAddInt方法问题来了这个unsafe对象又是什么?
sun.misc 下的Unsafe类 Java不能直接管理内存而Unsafe类可以帮助我们和C++那样来访问内存,而且Unsafe类中大多都是native方法
Unsafe类中getAndAddInt方法源码:
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
var5 = this.getIntVolatile(var1, var2);
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
return var5;
}
这就是原子类保证原子操作的核心代码了
首先do while循环很明显是自旋锁
然后compareAdnSwapInt是一个native方法,也就是它执行了一条cpu指令 该指令可以完成比较并且进行值交换操作,完成值的更新
unsafe.getAndAddInt(this, valueOffset, 1)
public final int getAndAddInt(Object var1, long var2, int var4)
var1代表当前的原子类对象->this对象
var2表示内存地址偏移量->valueOffset
var4则表示要增加的值 ->1
var5 = this.getIntVolatile(var1, var2)
根据传入的内存地址值 和偏移量得到原值
this.compareAndSwapInt(var1, var2, var5, var5 + var4)
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
本地方法通过c++调用底层硬件,该方法也就执行了一条指令该指令完成了"比较和交换一系列操作"
Object var1,就是传入的this对象 ->this
long var2就是内存地址偏移量->valueOffset
int var4就是内存原值->var5
int var5就是修改后的值->var5+1
ABA问题
CAS中会出现ABA问题,比如要修改A为B 但是中间进行了先将A修改为C然后又将C改回了A,最后进行A修改为B的操作的时候不知道进行了AC操作,这个未知的操作可能隐藏着问题。
解决ABA问题,使用到了乐观锁思想,每次修改就加上一个版本号做一个标记
在jdk 1.5之后为了解决ABA问题引入了AtomicStampedReferenc来解决ABA问题
示例演示了AtomicStampedReferenc可以解决ABA问题
public class ABA {
public static void main(String[] args) throws InterruptedException {
// 有参构造 传入初始值和初始的版本号
AtomicStampedReference<Integer> countStamped = new AtomicStampedReference<>(1, 1);
new Thread(()->{
int stamp = countStamped.getStamp();
System.out.println(Thread.currentThread().getName()+"------》期望的版本号"+stamp);
//获取到期望的版本号之后让正常线程休眠 让捣乱线程先去捣乱 修改版本号
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"------》实际的版本号"+countStamped.getStamp());
//由于捣乱线程先执行 导致版本号增加 与期望的版本号不一致导致修改失败
System.out.println(Thread.currentThread().getName()+"执行结果:"+countStamped.compareAndSet(1,6,stamp,stamp+1));
},"正常线程").start();
//先让 正常线程获取到期望的版本号
TimeUnit.SECONDS.sleep(1);
new Thread(()->{
//捣乱线程 先将1->2 又将2->1
System.out.println(Thread.currentThread().getName()+"执行结果:"+countStamped.compareAndSet(1,2,countStamped.getStamp(),countStamped.getStamp()+1));
System.out.println(Thread.currentThread().getName()+"执行结果:"+countStamped.compareAndSet(2,1,countStamped.getStamp(),countStamped.getStamp()+1));
},"捣乱线程").start();
}
}
测试结果:
显而易见正常线程执行失败 尽管值是我们所期望的 但是由于版本号不是我们期望的也会导致修改失败 从而解决了ABA问题
内容总结
以上是互联网集市为您收集整理的Java原子类中CAS详解全部内容,希望文章能够帮你解决Java原子类中CAS详解所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。