問題代碼現象
class MyThread extends Thread {
synchronized void fun1() { ... }
synchronized void fun2() { ... }
synchronized void fun3() { ... }
public synchronized void run() {
fun1(); fun2(); fun3();
}
}
Thread a = new MyThread("線程1");
Thread b = new MyThread("線程2");
a.start(); b.start();
// 輸出交替執行,未同步!
根本原因:鎖對象不同
synchronized 實例方法鎖的是 this(當前對象實例)。
a 和 b 是兩個不同的 MyThread 實例,各自擁有獨立的鎖。
a.run() 鎖 a,b.run() 鎖 b,互不干擾 → 並發執行。
正確同步方案
方案一:共享同一把鎖(傳入同一實例)
MyThread shared = new MyThread("共享");
Thread a = new Thread(shared); // 實現 Runnable 接口更合適
Thread b = new Thread(shared);
a.start(); b.start(); // 現在會串行執行 fun1→fun2→fun3
方案二:使用 static synchronized 鎖類對象
class MyThread extends Thread {
public static synchronized void fun1() { ... } // 鎖 MyThread.class
// 所有實例共享類鎖
}
方案三:顯式鎖對象(推薦)
class SharedResource {
private final Object lock = new Object();
public void fun1() { synchronized(lock) { ... } }
public void fun2() { synchronized(lock) { ... } }
}
// 多線程注入同一 SharedResource 實例
關鍵知識點總結
| 同步方式 | 鎖對象 | 作用範圍 |
| synchronized 實例方法 | this | 同一實例的所有同步方法互斥 |
| synchronized 靜態方法 | Class 對象 | 所有實例的該靜態方法互斥 |
| synchronized(this) | this | 同塊代碼互斥 |
| synchronized(共享對象) | 共享對象 | 持有該對象引用的線程互斥 |
最佳實踐
- 優先用
java.util.concurrent.Lock(ReentrantLock)替代 synchronized,支持超時、可中斷、公平鎖。
- 封裝共享狀態到線程安全類中,避免在調用方手動加鎖。
- 線程類繼承
Thread 不如實現 Runnable 靈活(支持線程池、共享實例)。