返回

深挖乐观锁思想在JAVA实现中的故事

后端

前言

生活中,我们看待事物总有不同的态度。比如,半瓶水,悲观的人会觉得只有半瓶水了,而乐观的人则会认为还有半瓶水呢。很多技术思想往往源于生活,因此在多个线程并发访问数据的时候,有了悲观锁和乐观锁。

悲观锁,顾名思义,就是悲观地认为并发操作一定会发生,因此在执行任何操作之前,都要先获得锁,然后再操作。这样可以保证数据的一致性,但也降低了并发效率。

乐观锁则相反,它假定并发操作不会发生,即使并发发生,也采用机制确保并发不会导致数据的不一致。乐观锁适用于大多数场景,实现简单,资源消耗低。

乐观锁思想在JAVA中的实现

乐观锁在JAVA中的实现主要有两种方式:CAS(Compare And Swap)和基于版本号的乐观锁。

CAS(Compare And Swap)

CAS是一种原子操作,它将内存中的一个值与给定值进行比较,如果相等,则将该值替换为新值,否则不执行任何操作。CAS操作通常用于实现无锁并发数据结构,如ConcurrentHashMap、ConcurrentSkipListMap等。

CAS操作的伪代码如下:

public static boolean compareAndSwap(int[] array, int index, int expectedValue, int newValue) {
    int oldValue = array[index];
    if (oldValue == expectedValue) {
        array[index] = newValue;
        return true;
    } else {
        return false;
    }
}

基于版本号的乐观锁

基于版本号的乐观锁是通过给数据增加一个版本号来实现的。每次数据被修改时,版本号都会随之增加。当一个线程要修改数据时,它会先读取数据的版本号,然后将数据修改后的新版本号与读取到的版本号进行比较。如果两个版本号相等,则说明数据没有被其他线程修改过,可以安全地进行修改。否则,说明数据已经被其他线程修改过,修改操作将失败。

基于版本号的乐观锁的伪代码如下:

public static boolean updateData(int[] array, int index, int expectedVersion, int newValue) {
    int[] data = array;
    int version = data[index + 1];
    if (version == expectedVersion) {
        data[index] = newValue;
        data[index + 1] = version + 1;
        return true;
    } else {
        return false;
    }
}

结语

乐观锁是一种乐观并发的实现思想,它假定并发操作不会发生,即使并发发生,也采用机制确保并发不会导致数据的不一致。乐观锁适用于大多数场景,实现简单,资源消耗低。

在JAVA中,乐观锁可以通过CAS(Compare And Swap)和基于版本号的乐观锁来实现。CAS操作可以实现无锁并发数据结构,而基于版本号的乐观锁则可以保证数据的并发一致性。