Java 函数中实现线程安全的两种方式:悲观锁:在访问数据前获取锁,防止其他线程并发访问,以确保数据一致性。(synchronized 关键字)乐观锁:在事务结束时验证数据,如果数据被修改则回滚事务,以提高并发性。(java.util.concurrent.atomic 包中的原子类)
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 函数中的悲观锁与乐观锁如何实现线程安全?的详细内容。更多信息请关注PHP中文网其他相关文章!