乐观锁是一种并发控制的机制,其核心思想是假设多个事务之间的冲突是不太可能发生的,因此在事务处理之前不会加锁,而是在事务提交的时候再检查是否有冲突。如果发现冲突,就会回滚事务,重新尝试。
Atomic 类提供了一些原子操作,如 compareAndSet。AtomicInteger、AtomicLong等。性能好: 在低并发环境下,乐观锁的性能通常优于悲观锁,因为不需要额外的加锁和解锁操作。
无阻塞: 由于乐观锁不会一开始就阻塞线程,因此适用于读操作较频繁、写操作较少的场景。
冲突处理: 当多个事务发生冲突时,需要进行冲突处理,通常是通过回滚事务,重新尝试。
不适用于高并发写操作: 当写操作较频繁时,乐观锁的性能可能下降,因为不断的冲突会导致事务的回滚和重试。
无法解决所有并发问题: 乐观锁机制不能解决所有并发问题,特别是在一些复杂的业务场景中。
- class Account {
- private String accountId;
- private double balance;
- private long version; // 版本号
-
- // 省略其他代码
-
- // 更新余额的方法
- public void updateBalance(double amount) {
- // 模拟乐观锁检查
- if (version != getAccountVersionFromDatabase()) {
- throw new OptimisticLockException("Concurrent modification detected");
- }
-
- // 更新余额
- this.balance += amount;
-
- // 更新版本号
- version++;
-
- // 更新数据库中的版本号和余额
- updateAccountInDatabase();
- }
- }
version 是账户对象的版本号,每次更新时都需要检查数据库中的版本号是否一致,如果不一致,则抛出乐观锁异常。
悲观锁是一种并发控制的机制,它的核心思想是在操作数据之前,悲观地认为会有并发操作的冲突,因此先进行加锁,确保每个时刻只有一个事务可以访问或修改共享资源。这种锁定机制确保了数据的一致性,但也可能导致性能的下降,因为多个事务可能需要等待锁的释放。
悲观锁的实现方式主要包括数据库锁、行级锁、表级锁等,以及编程语言级别的锁,如Java中的synchronized关键字、数据库中的SELECT ... FOR UPDATE等。
FOR UPDATE语句。synchronized关键字用于同步方法或代码块,确保在同一时刻只有一个线程可以访问被锁定的资源。数据一致性: 悲观锁确保了数据的一致性,因为在操作数据之前先获取了锁,避免了并发冲突。
简单直观: 实现相对简单,理解容易。
性能开销: 悲观锁的加锁操作会带来性能开销,尤其是在高并发的情况下,因为其他事务需要等待锁的释放。
死锁风险: 当多个事务相互等待对方释放锁时,可能发生死锁。
资源争用: 多个事务争用同一个资源时,可能导致大量的等待时间,降低系统的吞吐量。
在Java中,使用synchronized关键字可以实现悲观锁:
- public class BankAccount {
- private double balance;
-
- // 同步方法,使用悲观锁
- public synchronized void deposit(double amount) {
- balance += amount;
- }
-
- // 同步代码块,使用悲观锁
- public void withdraw(double amount) {
- synchronized (this) {
- if (balance >= amount) {
- balance -= amount;
- } else {
- System.out.println("Insufficient funds");
- }
- }
- }
- }
synchronized关键字确保在同一时刻只有一个线程可以执行deposit或withdraw方法。这就是一种悲观锁的实现方式。