Java 函数中的悲观锁与乐观锁如何实现线程安全?

2024年 5月 4日 109.2k 0

java 函数中实现线程安全的两种方式:悲观锁:在访问数据前获取锁,防止其他线程并发访问,以确保数据一致性。(synchronized 关键字)乐观锁:在事务结束时验证数据,如果数据被修改则回滚事务,以提高并发性。(java.util.concurrent.atomic 包中的原子类)

Java 函数中的悲观锁与乐观锁如何实现线程安全?

Java 函数中的悲观锁与乐观锁如何实现线程安全?

线程安全对于多线程环境至关重要,它确保了并发访问数据时数据的完整性和一致性。在 Java 中,悲观锁和乐观锁是实现线程安全的两大机制。下面我们将探讨它们的实现方式并提供实战案例。

悲观锁

悲观锁基于这样的假设:任何时候数据都可能被其他线程修改。因此,它在访问数据时立即获取锁,阻止其他线程访问数据,直到锁被释放。悲观锁的优点是能保证数据的一致性,缺点是可能导致锁竞争和死锁。

synchronized 关键字是 Java 中实现悲观锁的常用方法。它将代码块标记为临界区,只有获取锁的线程才能进入该代码块。

public class Counter {

    private int count;

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

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

乐观锁

乐观锁基于这样的假设:当线程访问数据时,数据不太可能被其他线程修改。它在事务结束时才对数据进行验证,如果数据被修改,则回滚事务。乐观锁的优点是能提高并发性,缺点是如果数据被修改,可能会导致事务失败。

在 Java 中,java.util.concurrent.atomic 包中的原子类可以实现乐观锁。原子类中的操作是原子性的,保证了并发访问数据的正确性。

import java.util.concurrent.atomic.AtomicInteger;

public class Counter {

    private AtomicInteger count = new AtomicInteger(0);

    public void increment() {
        count.incrementAndGet();
    }

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

实战案例:多线程银行账户

为了演示悲观锁和乐观锁在实际场景中的应用,我们考虑一个多线程银行账户。

  • 悲观锁实现:
public class BankAccount {

    private int balance;

    public synchronized void withdraw(int amount) {
        if (balance >= amount) {
            balance -= amount;
        }
    }

    public synchronized int getBalance() {
        return balance;
    }
}
  • 乐观锁实现:
import java.util.concurrent.atomic.AtomicInteger;

public class BankAccount {

    private AtomicInteger balance = new AtomicInteger(0);

    public void withdraw(int amount) {
        while (true) {
            int currentBalance = balance.get();
            if (currentBalance >= amount) {
                if (balance.compareAndSet(currentBalance, currentBalance - amount)) {
                    break;
                }
            } else {
                break;
            }
        }
    }

    public int getBalance() {
        return balance.get();
    }
}

使用 optimistic 锁,在取款时,它会获得当前余额,然后尝试使用 compareAndSet 原子地减去取款金额。如果余额不足,则该操作将失败,并且线程将重试。

选择悲观锁还是乐观锁

选择悲观锁还是乐观锁取决于具体场景。如果并发访问数据的情况很少,或者数据一致性十分关键,则悲观锁更合适。如果并发访问数据的情况频繁,并且数据一致性允许一定程度的折中,则乐观锁更合适。

以上就是Java 函数中的悲观锁与乐观锁如何实现线程安全?的详细内容,更多请关注每日运维网(www.mryunwei.com)其它相关文章!

相关文章

JavaScript2024新功能:Object.groupBy、正则表达式v标志
PHP trim 函数对多字节字符的使用和限制
新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
为React 19做准备:WordPress 6.6用户指南
如何删除WordPress中的所有评论

发布评论