Java并发编程实战~原子类

本文探讨了如何利用Java的AtomicLong和CAS(Check-And-Set)原理来实现无锁并发计数,包括原子类性能优化背后的硬件支持、ABA问题的解释以及getAndIncrement()方法的工作原理。同时介绍了Java 1.8中unsafe.getAndAddLong的用法,为理解并发控制提供深入剖析。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

对于简单的原子性问题,还有一种无锁方案,先看看如何利用原子类解决累加器问题。

public class Test {
	AtomicLong count = new AtomicLong(0);
	
	public void add10K() {
		int idx = 0;
		while(idx++ < 10000) {
			count.getAndIncrement();
		}
	}
	
}

无锁方案相对互斥锁方案,最大的好处就是性能。

无锁方案的实现原理

其实原子类性能高的秘密很简单,硬件支持而已。CPU 为了解决并发问题,提供了 CAS 指令(CAS,全称是 Compare And Swap,即“比较并交换”)。CAS 指令包含 3 个参数:共享变量的内存地址 A、用于比较的值 B 和共享变量的新值 C;并且只有当内存中地址 A 处的值等于 B 时,才能将内存中地址 A 处的值更新为新值 C。作为一条 CPU 指令,CAS 指令本身是能够保证原子性的。

你可以通过下面 CAS 指令的模拟代码来理解 CAS 的工作原理。在下面的模拟程序中有两个参数,一个是期望值 expect,另一个是需要写入的新值 newValue,只有当目前 count的值和期望值 expect 相等时,才会将 count 更新为 newValue

class SimulatedCAS{
	int count;
	
	public synchronized int cas(int expect, int newValue){
		// 读目前 count 的值
		int curValue = count;
		// 比较目前 count 值是否 == 期望值
		if(curValue == expect){
		// 如果是,则更新 count 的值
		count = newValue;
		}
		// 返回写入前的值
		return curValue;
	}
	
}

使用 CAS 来解决并发问题,一般都会伴随着自旋,而所谓自旋,其实就是循环尝试。

class SimulatedCAS{
	volatile int count;
	
	// 实现 count+=1
	public void addOne(){
		do {
			newValue = count+1; //①
		}while(count != cas(count,newValue) //②
	}
		
	// 模拟实现 CAS,仅用来帮助理解
	public synchronized int cas(int expect, int newValue){
		// 读目前 count 的值
		int curValue = count;
		// 比较目前 count 值是否 == 期望值
		if(curValue == expect){
			// 如果是,则更新 count 的值
			count= newValue;
		}
		// 返回写入前的值
		return curValue;
	}
}

但是在 CAS 方案中,有一个问题可能会常被你忽略,那就是ABA的问题。什么是 ABA 问题呢?

假设 count 原本是 A,线程 T1 在执行完代码①处之后,执行代码②处之前,有可能 count 被线程 T2 更新成了 B,之后又被 T3 更新回了 A,这样线程T1 虽然看到的一直是 A,但是其实已经被其他线程更新过了,这就是 ABA 问题。

Java 如何实现原子化的 count += 1

在 Java 1.8 版本中,getAndIncrement() 方法会转调 unsafe.getAndAddLong() 方法。这里 this 和 valueOffset 两个参数可以唯一确定共享变量的内存地址。

final long getAndIncrement() {
	return unsafe.getAndAddLong(this, valueOffset, 1L);
}

unsafe.getAndAddLong() 方法的源码如下

public final long getAndAddLong(Object o, long offset, long delta){
	long v;
	
	do {
		// 读取内存中的值
		v = getLongVolatile(o, offset);
	} while (!compareAndSwapLong(o, offset, v, v + delta));
	
	return v;
}

// 原子性地将变量更新为 x
// 条件是内存中的值等于 expected
// 更新成功则返回 true
native boolean compareAndSwapLong(Object o, long offset, long expected,long x);

原子类概览

 1. 原子化的基本数据类型

getAndIncrement() // 原子化 i++
getAndDecrement() // 原子化的 i--
incrementAndGet() // 原子化的 ++i
decrementAndGet() // 原子化的 --i
// 当前值 +=delta,返回 += 前的值
getAndAdd(delta) 
// 当前值 +=delta,返回 += 后的值
addAndGet(delta)
//CAS 操作,返回是否成功
compareAndSet(expect, update)
// 以下四个方法
// 新值可以通过传入 func 函数来计算
getAndUpdate(func)
updateAndGet(func)
getAndAccumulate(x,func)
accumulateAndGet(x,func)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值