怎么使用硬件同步原语替代锁?

什么是硬件同步原语?

硬件同步原语(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);
    }
posted @ 2023-03-19 09:13  李潘  阅读(273)  评论(0编辑  收藏  举报