Java 執行緒同步 (Synchronization)
當多個執行緒存取共享資源時,需要同步來避免競爭條件 (Race Condition)。
問題:競爭條件
public class Counter {
private int count = 0;
public void increment() {
count++; // 不是原子操作!
}
public int getCount() {
return count;
}
}
// 多執行緒同時呼叫 increment() 會有問題
Counter counter = new Counter();
Thread t1 = new Thread(() -> {
for (int i = 0; i < 10000; i++) counter.increment();
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 10000; i++) counter.increment();
});
t1.start();
t2.start();
t1.join();
t2.join();
// 結果可能不是 20000!
System.out.println(counter.getCount());
synchronized 方法
public class Counter {
private int count = 0;
// 同步方法
public synchronized void increment() {
count++;
}
public synchronized int getCount() {
return count;
}
}
synchronized 區塊
更細粒度的控制:
public class Counter {
private int count = 0;
private final Object lock = new Object();
public void increment() {
synchronized (lock) {
count++;
}
}
public int getCount() {
synchronized (lock) {
return count;
}
}
}
使用 this 作為鎖
public class Example {
public void method() {
synchronized (this) {
// 等同於 synchronized 方法
}
}
}
靜態同步
public class StaticCounter {
private static int count = 0;
// 鎖定的是 Class 物件
public static synchronized void increment() {
count++;
}
// 等同於
public static void increment2() {
synchronized (StaticCounter.class) {
count++;
}
}
}
Lock 介面
比 synchronized 更靈活:
import java.util.concurrent.locks.*;
public class Counter {
private int count = 0;
private final Lock lock = new ReentrantLock();
public void increment() {
lock.lock();
try {
count++;
} finally {
lock.unlock(); // 確保解鎖
}
}
}
tryLock
嘗試獲取鎖,不會阻塞:
if (lock.tryLock()) {
try {
// 取得鎖,執行操作
} finally {
lock.unlock();
}
} else {
// 無法取得鎖
}
// 帶有逾時
if (lock.tryLock(1, TimeUnit.SECONDS)) {
// ...
}
ReadWriteLock
讀寫分離:
ReadWriteLock rwLock = new ReentrantReadWriteLock();
Lock readLock = rwLock.readLock();
Lock writeLock = rwLock.writeLock();
// 讀取(可以多個執行緒同時讀)
readLock.lock();
try {
// 讀取操作
} finally {
readLock.unlock();
}
// 寫入(獨占)
writeLock.lock();
try {
// 寫入操作
} finally {
writeLock.unlock();
}
原子類別
import java.util.concurrent.atomic.*;
AtomicInteger count = new AtomicInteger(0);
count.incrementAndGet(); // ++count
count.getAndIncrement(); // count++
count.addAndGet(5); // count += 5
count.compareAndSet(5, 10); // CAS 操作
常用原子類別:
AtomicInteger、AtomicLong、AtomicBooleanAtomicReference<T>AtomicIntegerArray、AtomicLongArray
死鎖
兩個執行緒互相等待對方的鎖:
// 死鎖範例
Object lock1 = new Object();
Object lock2 = new Object();
Thread t1 = new Thread(() -> {
synchronized (lock1) {
Thread.sleep(100);
synchronized (lock2) {
// ...
}
}
});
Thread t2 = new Thread(() -> {
synchronized (lock2) {
Thread.sleep(100);
synchronized (lock1) {
// ...
}
}
});
// t1 等待 lock2,t2 等待 lock1 → 死鎖
避免死鎖
- 固定鎖的取得順序
- 使用 tryLock 設定逾時
- 避免巢狀鎖
synchronized vs Lock
| 特性 | synchronized | Lock |
|---|---|---|
| 自動釋放 | 是 | 否(需要 finally) |
| 中斷等待 | 否 | 是 |
| 嘗試取鎖 | 否 | 是(tryLock) |
| 公平性 | 否 | 可設定 |
| 多條件 | 一個 | 多個 Condition |
Condition
比 wait/notify 更靈活:
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
// 等待
lock.lock();
try {
while (!ready) {
condition.await(); // 類似 wait()
}
} finally {
lock.unlock();
}
// 通知
lock.lock();
try {
ready = true;
condition.signalAll(); // 類似 notifyAll()
} finally {
lock.unlock();
}