在Java中,悲觀鎖和樂觀鎖是處理併發訪問共享資源時採用的不同策略。它們主要的區別在於對資料競爭的預期和處理方式。
悲觀鎖 (Pessimistic Lock)
悲觀鎖基於“悲觀”的假設,即預設情況下它認為資料可能會被其他執行緒修改,因此在運算元據前會嘗試獲得獨佔的鎖。一旦某個執行緒持有悲觀鎖,其他試圖訪問相同資源的執行緒將被阻塞,直到鎖被釋放。
概念和作用:
悲觀鎖透過確保在任何時刻只有一個執行緒能夠訪問共享資源,從而避免了資料的競爭條件。它保證了資料的一致性和完整性,但是可能會降低系統的整體效能,因為等待鎖的執行緒會被阻塞。
示例程式碼:
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)演算法來實現。
示例程式碼:
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操作,它實現了樂觀鎖。如果兩個執行緒同時嘗試增加計數器的值,只有一個執行緒的更新會成功,另一個執行緒的更新會失敗,因為它檢測到資料已經發生了變化。