Java学习-------------乐观锁和悲观锁

         乐观锁和悲观锁是2种设计思想,前者认为在操作数据的时候,其他人不会同时修改,而后者则认为操作数据时,会被其他人同时修改。

        乐观锁通常是不上锁,先操作,再检查,即数据不会上锁,操作数据时无需进行获取锁和释放锁等操作,在操作完成之后进行检验,通过版本号,CAS或者其他方式判断是否被其他人操作过。而悲观锁就是先获取锁,再进行操作,操作结束后再释放锁,其他人才可以进行操作。

        从上面对两种锁的介绍,我们知道两种锁各有优缺点,不可认为一种好于另一种,像乐观锁适用于写比较少的情况下(多读场景),即冲突真的很少发生的时候,这样可以省去了锁的开销,加大了系统的整个吞吐量。但如果是多写的情况,一般会经常产生冲突,这就会导致上层应用会不断的进行retry,这样反倒是降低了性能,所以一般多写的场景下用悲观锁就比较合适。

        乐观锁采用CAS时会有一个ABA问题,即如果一个链表,其原本构造为A->B->C,如果这个时候有一个线程对其进行操作,在A的前面添加一个B,变成B->A->B->C,但这个时候有一个新的线程也调用了这个数据,在他的前面添加了一个A,变成了A->A->B->C,且新的线程比旧的线程更早的完成了修改,那么这个时候进行检查时会发现头指针任然是A,那么他有可能会认为其没被修改过,导致出现数据错误。所以通常我们会用版本号和CAS混合使用的方法,避免这个错误。

        下面是这两个锁的案例:

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 银行账户类(使用悲观锁)
 */
public class BankAccount {
    private final String accountId;
    private double balance;
    private final Lock lock = new ReentrantLock(); // 悲观锁
    
    public BankAccount(String accountId, double balance) {
        this.accountId = accountId;
        this.balance = balance;
    }
    
    /**
     * 转账操作(悲观锁实现)
     * @param toAccount 目标账户
     * @param amount 转账金额
     * @return 是否转账成功
     */
    public boolean transfer(BankAccount toAccount, double amount) {
        // 获取两个账户的锁(避免死锁,按固定顺序加锁)
        Lock firstLock = this.accountId.compareTo(toAccount.accountId) < 0 ? this.lock : toAccount.lock;
        Lock secondLock = this.accountId.compareTo(toAccount.accountId) < 0 ? toAccount.lock : this.lock;
        
        firstLock.lock();
        try {
            secondLock.lock();
            try {
                if (this.balance < amount) {
                    return false;
                }
                
                // 执行转账
                this.balance -= amount;
                toAccount.balance += amount;
                return true;
            } finally {
                secondLock.unlock();
            }
        } finally {
            firstLock.unlock();
        }
    }
    
}
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 商品库存类(使用乐观锁)
 */
public class ProductInventory {
    private final String productId;
    private final AtomicInteger stock; // 使用原子类实现乐观锁
    private final AtomicInteger version = new AtomicInteger(0); // 版本号
    
    public ProductInventory(String productId, int stock) {
        this.productId = productId;
        this.stock = new AtomicInteger(stock);
    }
    
    /**
     * 扣减库存(乐观锁实现)
     * @param quantity 扣减数量
     * @return 是否扣减成功
     */
    public boolean deductStock(int quantity) {
        while (true) {
            int currentStock = stock.get();
            int currentVersion = version.get();
            
            if (currentStock < quantity) {
                return false; // 库存不足
            }
            
            // CAS操作:比较库存和版本号,都未变时才更新
            if (stock.compareAndSet(currentStock, currentStock - quantity) {
                version.incrementAndGet(); // 更新版本号
                return true;
            }
            
            // 如果CAS失败,说明有其他线程修改了库存,循环重试
        }
    }
    
    /**
     * 更完善的乐观锁实现(带版本号检查)
     */
    public boolean deductStockWithVersion(int quantity) {
        while (true) {
            int currentStock = stock.get();
            int currentVersion = version.get();
            
            if (currentStock < quantity) {
                return false;
            }
            
            // 同时检查库存和版本号
            if (stock.compareAndSet(currentStock, currentStock - quantity) &&
                version.compareAndSet(currentVersion, currentVersion + 1)) {
                return true;
            }
        }
    }
    
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值