Java 函数中的悲观锁与乐观锁如何实现线程安全?
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 函数中的悲观锁与乐观锁如何实现线程安全?的详细内容,更多请关注每日运维网(www.mryunwei.com)其它相关文章!