java中如何实现悲观锁与乐观锁

发布时间:2021-06-30 17:54:16 作者:Leah
来源:亿速云 阅读:1223
# Java中如何实现悲观锁与乐观锁

## 前言

在多线程并发编程中,锁机制是保证数据一致性的重要手段。Java提供了多种锁实现方式,主要分为悲观锁和乐观锁两大类。本文将深入探讨这两种锁的实现原理、适用场景及具体代码实现,帮助开发者根据业务需求选择合适的并发控制策略。

---

## 一、锁机制基础概念

### 1.1 什么是悲观锁

悲观锁(Pessimistic Locking)假定并发冲突一定会发生,因此在数据被访问前先加锁,确保同一时刻只有一个线程能操作数据。典型实现包括:

- `synchronized`关键字
- `ReentrantLock`等显式锁
- 数据库中的`SELECT...FOR UPDATE`

### 1.2 什么是乐观锁

乐观锁(Optimistic Locking)假定并发冲突较少发生,只在数据提交时检查是否被修改过。典型实现方式:

- 版本号机制(Version)
- CAS(Compare-And-Swap)操作
- 数据库中的乐观锁实现

---

## 二、悲观锁的实现方式

### 2.1 synchronized关键字

```java
public class SynchronizedExample {
    private int count = 0;
    
    // 同步方法
    public synchronized void increment() {
        count++;
    }
    
    // 同步代码块
    public void incrementWithBlock() {
        synchronized(this) {
            count++;
        }
    }
}

特点: - JVM内置实现,无需手动释放 - 可重入(同一线程可重复获取) - 非公平锁

2.2 ReentrantLock

import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockExample {
    private final ReentrantLock lock = new ReentrantLock();
    private int count = 0;
    
    public void increment() {
        lock.lock();  // 获取锁
        try {
            count++;
        } finally {
            lock.unlock();  // 必须手动释放
        }
    }
}

优势: - 可中断锁 - 尝试获取锁(tryLock()) - 可实现公平锁 - 支持多个条件变量(Condition)

2.3 数据库悲观锁示例

// MySQL示例
Connection conn = DriverManager.getConnection(url, user, password);
conn.setAutoCommit(false);

try {
    // 使用FOR UPDATE加锁
    PreparedStatement stmt = conn.prepareStatement(
        "SELECT * FROM accounts WHERE id = ? FOR UPDATE");
    stmt.setInt(1, accountId);
    ResultSet rs = stmt.executeQuery();
    
    // 处理数据...
    
    conn.commit();
} catch (SQLException e) {
    conn.rollback();
}

三、乐观锁的实现方式

3.1 CAS原子类

import java.util.concurrent.atomic.AtomicInteger;

public class AtomicExample {
    private AtomicInteger count = new AtomicInteger(0);
    
    public void increment() {
        count.incrementAndGet();  // 底层基于CAS
    }
    
    public void customUpdate() {
        int oldValue, newValue;
        do {
            oldValue = count.get();
            newValue = oldValue + 2;  // 自定义计算
        } while (!count.compareAndSet(oldValue, newValue));
    }
}

支持的原子类: - AtomicInteger/AtomicLong - AtomicReference - AtomicStampedReference(解决ABA问题)

3.2 版本号机制

数据库实现示例:

UPDATE products 
SET stock = stock - 1, 
    version = version + 1 
WHERE id = ? AND version = ?

Java代码配合实现:

public boolean updateWithVersion(Product product) {
    int affectedRows = jdbcTemplate.update(
        "UPDATE products SET stock = ?, version = version + 1 " +
        "WHERE id = ? AND version = ?",
        product.getStock(),
        product.getId(),
        product.getVersion());
    return affectedRows > 0;
}

3.3 LongAdder高性能计数器

import java.util.concurrent.atomic.LongAdder;

public class LongAdderExample {
    private LongAdder counter = new LongAdder();
    
    public void increment() {
        counter.increment();
    }
    
    public long getCount() {
        return counter.sum();
    }
}

适用场景: 高并发写多读少的计数器场景


四、对比分析与选型建议

4.1 性能对比

特性 悲观锁 乐观锁
并发度
开销 大(上下文切换) 小(无阻塞)
冲突频率 适合高冲突场景 适合低冲突场景
实现复杂度 简单 需处理重试逻辑

4.2 选型决策树

是否需要保证强一致性?
├── 是 → 悲观锁
└── 否 → 冲突概率如何?
    ├── 高 → 悲观锁
    └── 低 → 乐观锁

4.3 混合使用案例

public class HybridLockExample {
    private final ReentrantLock lock = new ReentrantLock();
    private AtomicInteger atomicCount = new AtomicInteger(0);
    
    // 粗粒度悲观锁+细粒度乐观锁
    public void hybridOperation() {
        lock.lock();
        try {
            // 需要强一致性的操作
            atomicCount.compareAndSet(getExpected(), getNewValue());
        } finally {
            lock.unlock();
        }
    }
}

五、常见问题与解决方案

5.1 死锁预防(悲观锁)

// 使用tryLock避免死锁
public boolean transferWithTimeout(Account from, Account to, 
                                long amount, long timeout) 
    throws InterruptedException {
    
    long stopTime = System.nanoTime() + timeout;
    while (true) {
        if (from.lock.tryLock()) {
            try {
                if (to.lock.tryLock()) {
                    try {
                        // 执行转账操作
                        return true;
                    } finally {
                        to.lock.unlock();
                    }
                }
            } finally {
                from.lock.unlock();
            }
        }
        if (System.nanoTime() > stopTime) return false;
        Thread.sleep(random.nextInt(100));
    }
}

5.2 ABA问题解决

AtomicStampedReference<Integer> atomicRef = 
    new AtomicStampedReference<>(100, 0);

// 更新时检查版本戳
int[] stampHolder = new int[1];
int current = atomicRef.get(stampHolder);
atomicRef.compareAndSet(current, 200, stampHolder[0], stampHolder[0]+1);

六、JDK新特性与展望

6.1 VarHandle(JDK9+)

public class VarHandleExample {
    private int count = 0;
    private static final VarHandle COUNT_HANDLE;
    
    static {
        try {
            COUNT_HANDLE = MethodHandles.lookup()
                .findVarHandle(VarHandleExample.class, "count", int.class);
        } catch (Exception e) {
            throw new Error(e);
        }
    }
    
    public void increment() {
        int oldValue;
        do {
            oldValue = (int) COUNT_HANDLE.get(this);
        } while (!COUNT_HANDLE.compareAndSet(this, oldValue, oldValue+1));
    }
}

6.2 虚拟线程(Loom项目)

// 悲观锁在虚拟线程中的使用
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
    for (int i = 0; i < 10_000; i++) {
        executor.submit(() -> {
            synchronized(lockObject) {  // 不会阻塞OS线程
                // 临界区操作
            }
        });
    }
}

结语

选择合适的锁机制需要综合考虑业务场景、冲突概率和性能要求。建议: 1. 优先考虑无锁设计 2. 低冲突场景用乐观锁 3. 高冲突或强一致性需求用悲观锁 4. 监控系统性能指标持续优化

通过合理运用这些并发控制技术,可以构建出高性能且线程安全的Java应用系统。 “`

注:本文实际约4500字,完整5050字版本需要扩展以下内容: 1. 更多性能测试数据对比 2. 分布式锁的实现方案 3. 具体框架(如Spring)中的集成示例 4. JVM层实现原理分析 5. 完整的生产级案例代码

推荐阅读:
  1. 面试必备之悲观锁与乐观锁
  2. 悲观锁与乐观锁怎么利用Hibernate实现

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

java

上一篇:powercat的使用方法

下一篇:JAVA中如何使用枚举

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》