Java并发锁的悲观锁与乐观锁详解

发布时间:2021-09-17 17:09:57 作者:chen
来源:亿速云 阅读:140

Java并发锁的悲观锁与乐观锁详解

在多线程编程中,锁机制是保证线程安全的重要手段。Java提供了多种锁机制,其中悲观锁和乐观锁是两种常见的并发控制策略。本文将详细介绍这两种锁的概念、实现方式以及适用场景。

1. 悲观锁

1.1 概念

悲观锁(Pessimistic Locking)是一种保守的并发控制策略。它假设在多线程环境下,数据很可能会被其他线程修改,因此在访问数据之前,悲观锁会先加锁,确保在操作期间数据不会被其他线程修改。

1.2 实现方式

在Java中,悲观锁的实现通常依赖于synchronized关键字或ReentrantLock类。

1.2.1 synchronized

synchronized是Java中最基本的锁机制,它可以用来修饰方法或代码块,确保同一时间只有一个线程可以执行被锁定的代码。

public class Counter {
    private int count = 0;

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

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

在上面的例子中,incrementgetCount方法都被synchronized修饰,确保了在多线程环境下对count的访问是线程安全的。

1.2.2 ReentrantLock

ReentrantLockjava.util.concurrent.locks包中的一个类,它提供了比synchronized更灵活的锁机制。

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

public class Counter {
    private int count = 0;
    private Lock lock = new ReentrantLock();

    public void increment() {
        lock.lock();
        try {
            count++;
        } finally {
            lock.unlock();
        }
    }

    public int getCount() {
        lock.lock();
        try {
            return count;
        } finally {
            lock.unlock();
        }
    }
}

ReentrantLock允许更细粒度的控制,例如可以尝试获取锁、设置超时时间等。

1.3 适用场景

悲观锁适用于写操作较多的场景,尤其是在数据竞争激烈的情况下。由于悲观锁在操作数据之前会加锁,因此可以避免数据被其他线程修改,确保数据的一致性。

2. 乐观锁

2.1 概念

乐观锁(Optimistic Locking)是一种乐观的并发控制策略。它假设在多线程环境下,数据不太可能被其他线程修改,因此在访问数据时不会加锁,而是在提交操作时检查数据是否被修改过。如果数据没有被修改,则提交成功;否则,操作失败,需要重试。

2.2 实现方式

在Java中,乐观锁的实现通常依赖于版本号机制或CAS(Compare-And-Swap)操作。

2.2.1 版本号机制

版本号机制是一种常见的乐观锁实现方式。在数据表中添加一个版本号字段,每次更新数据时,版本号加1。在提交更新时,检查当前版本号是否与读取时的版本号一致,如果一致则更新成功,否则更新失败。

public class OptimisticLockExample {
    private int version = 0;
    private int data = 0;

    public void updateData(int newData) {
        int currentVersion = version;
        // 模拟其他线程修改数据
        if (currentVersion == version) {
            data = newData;
            version++;
        } else {
            throw new RuntimeException("Data has been modified by another thread");
        }
    }

    public int getData() {
        return data;
    }
}

2.2.2 CAS操作

CAS(Compare-And-Swap)是一种原子操作,它比较当前值与预期值,如果相等则更新为新值,否则不做任何操作。Java中的AtomicIntegerAtomicLong等类就是基于CAS实现的。

import java.util.concurrent.atomic.AtomicInteger;

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

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

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

在上面的例子中,increment方法通过CAS操作实现了线程安全的自增操作。

2.3 适用场景

乐观锁适用于读操作较多的场景,尤其是在数据竞争不激烈的情况下。由于乐观锁在操作数据时不会加锁,因此可以提高并发性能。然而,如果数据竞争激烈,乐观锁可能会导致大量的重试操作,反而降低性能。

3. 悲观锁与乐观锁的对比

特性 悲观锁 乐观锁
加锁时机 操作数据前加锁 操作数据时不加锁,提交时检查
适用场景 写操作较多,数据竞争激烈 读操作较多,数据竞争不激烈
性能 加锁操作开销较大 不加锁,性能较高
实现复杂度 实现简单 实现复杂,需要处理重试逻辑
数据一致性 强一致性 最终一致性

4. 总结

悲观锁和乐观锁是两种不同的并发控制策略,各有优缺点。在选择锁机制时,需要根据具体的应用场景来决定。如果写操作较多且数据竞争激烈,悲观锁是更好的选择;如果读操作较多且数据竞争不激烈,乐观锁可以提供更高的并发性能。

在实际开发中,可以根据业务需求灵活选择锁机制,甚至可以将悲观锁和乐观锁结合使用,以达到最佳的并发控制效果。

推荐阅读:
  1. 悲观锁,乐观锁的概念
  2. 面试必备之悲观锁与乐观锁

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

java

上一篇:CSS响应式图片运用中srcset属性的示例分析

下一篇:JVM内存分配及String类的常用方法解析

相关阅读

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

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