當多執行緒同時存取共享可變資料時,會發生競態條件導致資料不一致。Java 提供 synchronized 關鍵字實現內建鎖,確保同一時間僅有一條執行緒執行受保護的程式碼區塊。
public class SyThreadDemo {
public static void main(String[] args) {
trade ft = new trade(); // 共享資料物件
addThread tt1 = new addThread(ft, "add");
decThread tt2 = new decThread(ft, "dec");
tt1.start();
tt2.start();
}
}
// 同步類別:封裝共享資料與同步操作
class trade {
private String transName;
private double amount;
// synchronized 修飾實例方法 → 鎖定 this 物件
synchronized void update(String transName, double amount) {
this.transName = transName;
this.amount = amount;
System.out.println(this.transName + " " + this.amount);
}
}
// 增加資金執行緒
class addThread extends Thread {
private trade ft;
addThread(trade ft, String name) { super(name); this.ft = ft; }
public void run() {
for (int i = 0; i < 10; i++)
ft.update("add", 2000.0);
}
}
// 減少資金執行緒
class decThread extends Thread {
private trade fd;
decThread(trade fd, String name) { super(name); this.fd = fd; }
public void run() {
for (int i = 0; i < 10; i++)
fd.update("dec", -2000.0);
}
}
add 2000.0 dec -2000.0 add 2000.0 dec -2000.0 ... add 2000.0 dec -2000.0
| 用法 | 鎖定物件 | 適用場景 |
|---|---|---|
synchronized 修飾實例方法 | this(目前實例) | 保護該實例的非靜態欄位 |
synchronized 修飾靜態方法 | 類別物件(ClassName.class) | 保護靜態欄位 |
synchronized(obj) { ... } | obj(指定物件) | 細粒度鎖、跨實例協調 |
trade.update() 為實例同步方法 → 鎖定 trade 實例(ft/fd 指向同一物件)transName 與 amount 成對更新synchronized,輸出會出現「add -2000.0」等錯亂組合synchronized(new Object()) 每次建立新鎖,失去保護效果String、Integer 等可能被常數池共用,導致非預期互斥private final Object lock = new Object();// 显式鎖:更靈活,支援可中斷、超時、公平鎖
private final ReentrantLock lock = new ReentrantLock();
public void update(...) {
lock.lock();
try { /* 關鍵區 */ } finally { lock.unlock(); }
}
// 原子類別:無鎖執行緒安全
private final AtomicReference name = new AtomicReference<>();
private final AtomicLong amount = new AtomicLong();
掌握 synchronized 是理解 Java 並發基石,進而駕馭 JUC 套件(Lock、Atomic*、ConcurrentHashMap 等)的前提。