简单理解:
悲观锁:假设每次都会发生并发冲突,所以每次都上锁,适用于写多读少的情况,对于高并发场景不友好,高并发的场景下频繁地对代码加锁会影响其并发性能,可能造成拥塞;
乐观锁:假设不会发生冲突,只有当产生冲突的时候才进行回溯,适合读多写少的情况;乐观锁控制冲突的原理是依靠一个数据版本,每次在开始使用的时候给数据一个版本号,而提交的时候再去看这个版本号是否发生变化,以确定是否产生冲突。因此,乐观锁不适用于长事务,因为长事务可能会造成提交的时候反复回滚;
以银行的存取存款和读取为例,悲观锁和乐观锁在java中的实现:
悲观锁:
public class BankAccountPessimistic {
private int balance;
public BankAccountPessimistic(int initialBalance) {
this.balance = initialBalance;
}
// 悲观锁:使用synchronized实现
public synchronized void deposit(int amount) {
balance += amount;
System.out.println(Thread.currentThread().getName() + " deposited " + amount + ", balance: " + balance);
}
public synchronized void withdraw(int amount) {
if (balance >= amount) {
balance -= amount;
System.out.println(Thread.currentThread().getName() + " withdrew " + amount + ", balance: " + balance);
} else {
System.out.println(Thread.currentThread().getName() + " failed to withdraw " + amount + ", insufficient balance.");
}
}
public synchronized int getBalance() {
return balance;
}
public static void main(String[] args) {
BankAccountPessimistic account = new BankAccountPessimistic(100);
Thread t1 = new Thread(() -> {
account.deposit(50);
account.withdraw(30);
}, "Thread 1");
Thread t2 = new Thread(() -> {
account.withdraw(70);
account.deposit(20);
}, "Thread 2");
t1.start();
t2.start();
}
}
乐观锁:
import java.util.concurrent.atomic.AtomicInteger;
public class BankAccountOptimistic {
private AtomicInteger balance;
private AtomicInteger version;
public BankAccountOptimistic(int initialBalance) {
this.balance = new AtomicInteger(initialBalance);
this.version = new AtomicInteger(0);
}
// 乐观锁:使用AtomicInteger的compareAndSet实现
public void deposit(int amount) {
int currentVersion;
int newBalance;
do {
currentVersion = version.get();
newBalance = balance.get() + amount;
} while (!version.compareAndSet(currentVersion, currentVersion + 1));
balance.set(newBalance);
System.out.println(Thread.currentThread().getName() + " deposited " + amount + ", balance: " + balance.get());
}
public void withdraw(int amount) {
int currentVersion;
int newBalance;
do {
currentVersion = version.get();
if (balance.get() < amount) {
System.out.println(Thread.currentThread().getName() + " failed to withdraw " + amount + ", insufficient balance.");
return;
}
newBalance = balance.get() - amount;
} while (!version.compareAndSet(currentVersion, currentVersion + 1));
balance.set(newBalance);
System.out.println(Thread.currentThread().getName() + " withdrew " + amount + ", balance: " + balance.get());
}
public int getBalance() {
return balance.get();
}
public static void main(String[] args) {
BankAccountOptimistic account = new BankAccountOptimistic(100);
Thread t1 = new Thread(() -> {
account.deposit(50);
account.withdraw(30);
}, "Thread 1");
Thread t2 = new Thread(() -> {
account.withdraw(70);
account.deposit(20);
}, "Thread 2");
t1.start();
t2.start();
}
}