在Java中,悲观锁和乐观锁是处理并发访问共享资源时采用的不同策略。它们主要的区别在于对数据竞争的预期和处理方式。
悲观锁 (Pessimistic Lock)
悲观锁基于“悲观”的假设,即默认情况下它认为数据可能会被其他线程修改,因此在操作数据前会尝试获得独占的锁。一旦某个线程持有悲观锁,其他试图访问相同资源的线程将被阻塞,直到锁被释放。
概念和作用: 悲观锁通过确保在任何时刻只有一个线程能够访问共享资源,从而避免了数据的竞争条件。它保证了数据的一致性和完整性,但是可能会降低系统的整体性能,因为等待锁的线程会被阻塞。
示例代码:
代码语言:javascript复制public class PessimisticLockExample {
public static void main(String[] args) {
Object lock = new Object();
Thread thread1 = new Thread(() -> {
synchronized (lock) { // 悲观锁
System.out.println("Thread 1: Acquired lock");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread 1: Releasing lock");
}
});
Thread thread2 = new Thread(() -> {
synchronized (lock) { // 悲观锁
System.out.println("Thread 2: Attempting to acquire lock");
}
});
thread1.start();
thread2.start();
}
}
在这个例子中,thread2
在thread1
释放锁之前将被阻塞,无法执行其代码。
乐观锁 (Optimistic Lock)
乐观锁基于“乐观”的假设,即默认情况下它认为数据不会被其他线程修改,因此在操作数据时不立即加锁。当需要更新数据时,它会检查在此期间数据是否已经被其他线程修改过。如果数据未被修改,则更新成功;如果数据已被修改,则更新失败,并可能触发重试。
概念和作用: 乐观锁主要用于减少锁带来的性能开销,尤其是在读操作远多于写操作的场景下。它通过避免不必要的锁争用来提高并发性能,但这也意味着在数据确实被其他线程修改时,操作可能会失败。
实现方式: 乐观锁可以通过版本号机制或CAS(Compare and Swap)算法来实现。
示例代码:
代码语言:javascript复制import java.util.concurrent.atomic.AtomicInteger;
public class OptimisticLockExample {
public static void main(String[] args) {
AtomicInteger counter = new AtomicInteger(0); // 使用乐观锁
Thread thread1 = new Thread(() -> {
int oldValue = counter.get();
// 假设这里有一段长的计算过程...
int newValue = oldValue 1;
boolean success = counter.compareAndSet(oldValue, newValue); // CAS操作
if (success) {
System.out.println("Thread 1: Incremented value to " newValue);
} else {
System.out.println("Thread 1: Failed to increment value.");
}
});
Thread thread2 = new Thread(() -> {
int oldValue = counter.get();
// 假设这里有一段长的计算过程...
int newValue = oldValue 1;
boolean success = counter.compareAndSet(oldValue, newValue); // CAS操作
if (success) {
System.out.println("Thread 2: Incremented value to " newValue);
} else {
System.out.println("Thread 2: Failed to increment value.");
}
});
thread1.start();
thread2.start();
}
}
在这个例子中,AtomicInteger
的compareAndSet
方法是一个CAS操作,它实现了乐观锁。如果两个线程同时尝试增加计数器的值,只有一个线程的更新会成功,另一个线程的更新会失败,因为它检测到数据已经发生了变化。