Java 乐观锁的介绍以及对compareAndSet方法的使用及理解(以AtomicBoolean为例)

2022/1/14 12:03:29

本文主要是介绍Java 乐观锁的介绍以及对compareAndSet方法的使用及理解(以AtomicBoolean为例),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

1.乐观锁是什么

乐观锁可以理解为生活中乐观的人,这种人总是想着事情会往好的方向发展。与其对应的是悲观锁,同理悲观锁对应生活中悲观的人,他们总是认为事情会往坏的方向发展。两种锁没有绝对的好坏之分。他们的好坏取决于应用场景。

正如文章开头说的那样,乐观锁总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁。但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,如果没有人更新这个数据的话,就会使用原子操作去更新自己的修改,否则就不执行任何操作。这个过程可以使用两种方式实现。即版本号机制和CAS算法(compare and swap 比较与交换)。

1. 版本号机制
因为本节的重点是讲Java的乐观锁,而java的原子变量类(java.util.concurrent.atomic)是使用CAS算法实现的,所以版本号机制咱们仅作介绍,需要深入理解的可以自行查找资料哦。
版本号机制一般用于数据库,在数据库表中加一个数据版本version字段表示数据被修改的次数,当数据被修改时,version值会加一。当线程A想要更新数据值时,在读取数据的同时也会去读取version值,在提交更新时,若刚才读取的version值与当前数据库中的version值相等,则提交跟新自己的
2. CAS算法
CAS即Compare And Swap(比较与交换),是一种无锁算法,即在不使用锁的情况下实现多线程之间变量的同步,所以也叫非阻塞同步(Non- blocking Synchronization),CAS的执行过程如下图所示:

在这里插入图片描述当且仅当V的值和新值N相等时,CAS通过原子操作的方式用计算结果值A来更新V的值,否则不会执行任何操作。一般情况下是自旋操作,也就是不断的重试

3. 乐观锁的缺点

(1) ABA问题
即如果一个变量V初次读取时值是A,并且在准备赋值时检查它的值仍然是A,那能说明它的值没有被其他线程修改过吗?显然不能。因为可能在准备赋值前,V的值被其他线程修改成了其他值B,然后又修改成了A,那么CAS操作就会误认为它从来没有被修改过,这个就是CAS操作的**“ABA”问题**

(2)循环时间长开销大
自旋CAS,不成功就会一直循环执行直到成功,如果长时间不成功,会给CPU带来很大的开销

(3)只能保证一个共享变量的原子操作
CAS只对单个共享变量有效,当操作涉及到多个共享变量时CAS无效。但在JDK1.5开始,提供了AtomicReference类来保证引用对象之间的原子性,可以把多个变量放在一个对象里进行CAS操作。所以可以使用锁或者是利用AtomicReference类把多个共享变量合并成一个共享变量来操作

3.乐观锁使用场景

CAS适用于线程冲突较少的情景,一般在写操作比较少,读操作比较多的情况下推荐使用

Demo理解CompareAndSet()方法

    public static void main(String[] args) {
        AtomicBoolean atomicBoolean = new AtomicBoolean(false);
        System.out.println("=====================================");
        System.out.println("result==>"+atomicBoolean.compareAndSet(true,false));
        System.out.println("current value: " + atomicBoolean.get());//false

        System.out.println("=====================================");
        System.out.println("result1==>"+atomicBoolean.compareAndSet(true,true));
        System.out.println("current1 value: " + atomicBoolean.get());//false

        System.out.println("=====================================");
        System.out.println("result2==>"+atomicBoolean.compareAndSet(false,false));
        System.out.println("current2 value: " + atomicBoolean.get());//false

        System.out.println("=====================================");
        System.out.println("result3==>"+atomicBoolean.compareAndSet(false,true));
        System.out.println("current3 value: " + atomicBoolean.get());//true
        System.out.println("=====================================");

        System.out.println("=====================================");
        System.out.println("result4==>"+atomicBoolean.compareAndSet(true,false));
        System.out.println("current4 value: " + atomicBoolean.get());//false
        System.out.println("=====================================");

    }

需要注意的是compareAndSet()方法返回的是操作的结果,操作成功或者是失败,如:

方法的说明如下:

/**
     * Atomically sets the value to the given updated value
     * if the current value {@code ==} the expected value.
     *
     * @param expect the expected value
     * @param update the new value
     * @return {@code true} if successful. False return indicates that
     * the actual value was not equal to the expected value.
     */
    public final boolean compareAndSet(boolean expect, boolean update) {
        return U.compareAndSwapInt(this, VALUE,
                                   (expect ? 1 : 0),
                                   (update ? 1 : 0));
    }
 AtomicBoolean atomicBoolean
  = new AtomicBoolean(false);
atomicBoolean.compareAndSet(true,false)

这段代码,读出的初始值是false,但是第一个参数的值代表期待的值(expect),只有当这个expect值和当前从内存中读出的值相等时,才会用新值(update)去更新当前内存中的值,返回操作成功的结果为true,否则返回false,表示操作不成功。所以上面的代码会返回false,内存中现在的值不变(因为没有更新成功),还是false

 AtomicBoolean atomicBoolean
  = new AtomicBoolean(true);
atomicBoolean.compareAndSet(true,false)

再看这段代码,从内存中读出的初始值为true,expect的值也为true,此时就可以用update的值(false)更新现在内存中的值,返回true表示操作成功。所以上面这段代码会返回true,内存中的值为false,因为更新成功了



这篇关于Java 乐观锁的介绍以及对compareAndSet方法的使用及理解(以AtomicBoolean为例)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程