乐观锁 & 悲观锁
悲观锁
悲观锁的核心思想是在共享资源被操作时假设冲突一定会发生全部都加锁.
实现方式
- synchronized : Java 中的关键字,可以修饰方法或代码块,保证同一时刻只有一个线程可以执行被修饰的代码。
- ReentrantLock: Java
java.util.concurrent.locks包下的一个类,提供了比synchronized更广泛的锁操作。例如,它可以实现公平锁、非公平锁,可以被中断地获取锁等。
缺点
- 多个线程在持有锁并请求对方已持有的锁时,会造成相互等待,导致所有线程都无法继续执行。
- 当一个线程获取锁失败后,会被阻塞(挂起),操作系统需要将其从运行态切换到等待态。当锁被释放时,又需要将等待的线程唤醒,这个线程上下文的切换会消耗一定的 CPU 资源。
- 只要有一个线程持有了锁,其他所有想要访问该资源的线程都必须等待,这会降低系统的并发性能和吞吐量。
适用场景
适合写多读少的情况,因为读操作并不对资源进行修改不会出现线程问题,而写操作要求强数值一致性所以适合悲观锁.
乐观锁
它总是假设最好的情况,认为数据在被自己操作时不会被其他线程修改。所以它不会加锁,而是在更新数据时,去检查在此期间有没有其他线程修改过数据。
实现方式
concurrent.atomic: Java 的 java.util.concurrent.atomic 包下的一系列原子类,如 AtomicInteger, AtomicLong 等。它们内部使用了 CAS 机制来实现原子操作。
机制
版本控制
在数据库中加入版本属性,当线程操作时需要携带当前操作数据的版本号,操作完成后比较此时版本与数据库中版本是否相同.
CAS(Compare And Swap)
CAS 是一个原子操作,底层依赖于一条 CPU 的原子指令。
原子操作 即最小不可拆分的操作,也就是说操作一旦开始,就不能被打断,直到操作完成。
CAS 涉及到三个操作数:
- V:要更新的变量值(Var)
- E:预期值(Expected)
- N:拟写入的新值(New)
当且仅当 V 的值等于 E 时,CAS 通过原子方式用新值 N 来更新 V 的值。如果不等,说明已经有其它线程更新了 V,则当前线程放弃更新。