怎么使用硬件同步原语替代锁?
什么是硬件同步原语?
硬件同步原语(Atomic Hardware Primitives)是由计算机硬件提供的一组原子操作。
我们常见的原语操作有CAS和FAA两种。
CAS
Compare and Swap(CAS),字面意思是先比较,再计算。它的伪代码如下。
<< atomic >>
function cas(p : pointer to int, old : int, new : int) returns bool {
if *p ≠ old {
return false
}
*p ← new
return true
}
它的输入参数有三个:
- p:要修改的变量的指针
- old:旧值
- new:新值
返回值是一个布尔值,标识是否赋值成功。
FAA
Fetch and Add(FAA),它的含义是先获取变量当前的值value,然后给变量p增加inc,最后返回变量p之前的值value。
它的伪代码如下:
<< atomic >>
function faa(p : pointer to int, inc : int) returns int {
int value <- *location
*p <- value + inc
return value
}
上述两段伪代码,它们的特殊之处在于它们都是由计算机硬件,具体讲就是CPU提供实现,可以保证操作的原子性。
因为原子操作具有不可分割性,也就不存在并发的问题,所以在某些情况下,原语可以用来替代锁,实现一些安全又高效的并发操作。
CAS和FAA在各种编程语言中,都有相应的实现,可以直接使用,无论哪种编程语言,它们底层的实现是一样的,效果也是一样的。
Java中的操作原语
Java语言本身支持操作原语,Java中的CAS可以理解为乐观锁,java.util.concurrent.atomic包下面的类已经实现了乐观锁,其中最常用的是AtomicInteger,可以将其看做实现了CAS操作的Integer。
下面是使用AtomicInteger的示例代码。
private static AtomicInteger count = new AtomicInteger(0);
public static void main(String[] args) {
for (int i = 0; i < 2; i++) {
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(10);
} catch (Exception e) {
e.printStackTrace();
}
//每个线程让count自增100次
for (int i = 0; i < 100; i++) {
count.incrementAndGet();
}
}
}).start();
}
try{
Thread.sleep(2000);
}catch (Exception e){
e.printStackTrace();
}
System.out.println(count);
}