马士兵java架构师

您现在的位置是:java学习笔记 >

java学习笔记

java悲观锁乐观锁定义

2024-04-10 12:17:42java学习笔记 本文浏览次数:0 百度已收录

本 文 目 录

java悲观锁乐观锁定义

引言

在多线程并发编程的世界里,锁机制是保证数据一致性和操作原子性的重要手段。Java提供了多种锁机制,其中悲观锁和乐观锁是两种常见的并发控制方式。它们在处理并发问题时有着截然不同的策略和应用场景。本文将深入探讨这两种锁的定义、目的、条件、区别与不同,并通过代码案例来展示它们在实际开发中的应用。

悲观锁与乐观锁的定义与目的

悲观锁

悲观锁的核心理念是假设并发环境下,数据极有可能被多个线程同时修改,因此在访问数据时必须通过加锁来保证数据的一致性【3】。悲观锁的实现通常依赖于synchronized关键字或Lock接口,其目的是通过独占资源的方式防止数据冲突,确保操作的安全性【3】。

乐观锁

与悲观锁相反,乐观锁基于乐观的假设,认为数据在大多数情况下不会被其他线程修改,因此不需要加锁即可进行操作。只有在数据实际发生冲突时,才进行重试或其他补偿措施【3】。乐观锁的实现通常依赖于版本号或时间戳机制,通过检查数据是否被其他线程更新过来判断是否发生冲突【4】。

悲观锁与乐观锁的条件与区别

条件

  • 悲观锁条件:高并发场景下,数据频繁被多个线程读写,且存在大量更新操作【3】。
  • 乐观锁条件:读操作远多于写操作,或者更新操作的概率较低的场景【4】。

区别

  • 锁策略:悲观锁采取先占策略,乐观锁采取后验策略【3】。
  • 性能开销:悲观锁可能导致线程挂起和上下文切换,乐观锁通常性能更高,但在高冲突下可能需要频繁重试【4】【5】。
  • 实现方式:悲观锁通过阻塞机制实现,乐观锁通过版本控制和CAS(Compare And Swap)操作实现【3】【4】。

核心类与方法

悲观锁

  • synchronized关键字:可以用来修饰方法或代码块,实现简单的悲观锁【3】。
  • ReentrantLock类:实现了Lock接口,提供了更灵活的锁机制,如可重入、可中断、公平性等【3】。

乐观锁

  • AtomicInteger类:提供了原子性的整数操作,是实现乐观锁的常用类之一【4】。
  • ConcurrentHashMap类:使用分离锁和CAS操作,实现了线程安全的哈希表【6】。

使用场景

悲观锁使用场景

  • 金融交易系统:在转账等操作中,需要严格保证数据的一致性,适合使用悲观锁【5】。

乐观锁使用场景

  • 缓存系统:在读多写少的场景下,乐观锁可以减少锁的竞争,提高系统的吞吐量【5】。

代码案例

悲观锁代码案例

public class PessimisticLock {
    private final Object lock = new Object();
    private int count = 0;

    public void increment() {
        synchronized (lock) {
            count++;
        }
    }

    public int getCount() {
        synchronized (lock) {
            return count;
        }
    }
}

乐观锁代码案例

public class OptimisticLock {
    private AtomicInteger count = new AtomicInteger(0);

    public void increment() {
        int current, update;
        do {
            current = count.get();
            update = current + 1;
        } while (!count.compareAndSet(current, update));
    }

    public int getCount() {
        return count.get();
    }
}

对比表格

特性 悲观锁 乐观锁
核心理念 假设数据会被修改 假设数据不会被修改
性能 可能因线程阻塞导致性能下降 通常性能更高,高冲突时可能需要频繁重试
实现方式 synchronized, Lock接口 版本号控制,CAS操作
适用场景 高并发更新操作频繁 读多写少,更新操作概率低

结语

悲观锁和乐观锁各有优势和适用场景。在实际开发中,我们需要根据业务需求和系统特点来选择合适的锁机制。悲观锁适合于更新操作频繁的场景,而乐观锁则适用于读操作多、冲突概率低的场景。通过合理地使用这两种锁,我们可以有效地解决并发编程中的各种问题,保证数据的一致性和操作的原子性。