20190708 - Java 乐观锁 VS 悲观锁

乐观锁 VS 悲观锁

悲观锁:认为自己在使用数据的时候一定会有线程来修改数据,因此在获取数据的时候会先加锁,确保数据不会被其他线程所修改。(例如:synchronized、lock)
乐观锁:认为自己在使用数据的时候不会有其他线程来修改数据,所以不会添加锁,只是会在更新数据的之前判断有没有被其他线程更新,如果没有,则执行更新,如果有,则采用其他方式(报错或重试等)。

乐观锁在Java中是通过使用无锁编程来实现,最常采用的是CAS算法。(CAS:Compare and Swap,即比较再交换。)
在这里插入图片描述
所以说,悲观锁适合写操作多的场景,先加锁可以保证写操作时数据正确。
乐观锁适合读操作多的场景,没有锁的操作可以提升性能。

/**
 * @description: 乐观锁
 * @author: annecheng,2019-07-08 10:33
 */
public class OptimisticLock {
    //AtomicInteger 原子操作类
    private AtomicInteger atomicInteger = new AtomicInteger();
    private void testOptimisticLock() {
        atomicInteger.incrementAndGet();
    }
}

/**
 * @description: 悲观锁
 * @author: annecheng,2019-07-08 10:32
 */
public class PessimisticLock {
    private synchronized void testMethod(){
        System.out.println("操作同步资源");
    }
    //如果一个线程已经获得了锁,其内部还可以多次申请该锁成功,那么该锁为可重入锁。
    private ReentrantLock lock = new ReentrantLock();
    public void testPessimisticLock(){
        lock.lock();
        this.testMethod();
        lock.unlock();
    }
}

乐观锁的实现:CAS

CAS,Compare And Swap(比较和交换),无锁算法,在不使用锁的情况下,实现多线程之间的变量同步。是一个原子性的操作,AtomicInteger就是一种。
AtomicInteger 源码:
在这里插入图片描述
属性:
unsafe: 获取并操作内存的数据。
valueOffset: 存储value在AtomicInteger中的偏移量。
value: 存储AtomicInteger的int值,该属性需要借助volatile关键字保证其在线程间是可见的。
AtomicInteger的incrementAndGet方法底层调用unsafe.compareAndSwapInt()方法;在这里插入图片描述
getAndAddInt(),给对象o加上偏移量offest,是否等于v,如果相等,设置成v+delta,如果不相等,一直循环重试。

CAS存在的问题:
1、ABA问题,在操作值的时候检查内存值是否发生变化,但是值可能是从A变到B再变回A,那么CAS是检测不到变化的。所以,应该再加一个版本号进行控制。JDK的解决ABA办法:AtomicStampedReference。
2、循环时间长开销带,CAS操作如果长时间不成功,会导致一直自旋,CPU开销很大。
3、只能保证一个共享变量的原子操作。对一个共享变量执行操作时,CAS能够保证原子操作,但是对多个共享变量操作时,CAS是无法保证操作的原子性的。JDK提供了AtomicReference类来保证引用对象之间的原子性。

参考链接:https://mp.weixin.qq.com/s/E2fOUHOabm10k_EVugX08g

原创文章 88 获赞 21 访问量 3万+

猜你喜欢

转载自blog.csdn.net/cfy1024/article/details/95043624
今日推荐