synchronized 關鍵字既可修飾方法,也可作為代碼塊使用。同步代碼塊允許精確控制鎖範圍,將非臨界區代碼移出同步塊,顯著提升並發吞吐。
// 同步方法:整個方法加鎖,耗時 6 秒
public synchronized void doLongTask() {
Thread.sleep(3000); // 非臨界區也被鎖住
// 臨界區操作
}
// 同步代碼塊:僅臨界區加鎖,耗時 3 秒
public void doLongTask() {
Thread.sleep(3000); // 異步執行,無鎖
synchronized(this) { // 僅共享數據賦值加鎖
this.data1 = result1;
this.data2 = result2;
}
}
同一對象的多個 synchronized(this) 代碼塊互斥,共享同一對象監視器。不同對象的同步塊不互斥。
public class Service {
public void methodA() {
synchronized(this) { sleep(2000); } // 鎖 this
}
public void methodB() {
synchronized(this) { /* 邏輯 */ } // 鎖 this,與 A 互斥
}
}
synchronized static void method():鎖 ClassName.class 對象synchronized(ClassName.class) { }:等效代碼塊寫法public class Demo {
synchronized static void printA() { sleep(3000); } // 鎖 Demo.class
synchronized static void printB() { sleep(3000); } // 鎖 Demo.class,與 A 互斥
synchronized void printC() { sleep(3000); } // 鎖 this,與 A/B 不互斥
}
內部類實例擁有獨立的 this 引用,其同步鎖與外部類實例鎖完全無關。
public class Outer {
static class Inner1 {
public void method1(Inner2 i2) {
synchronized(i2) { /* 鎖 Inner2 實例 */ }
}
public synchronized void method2() { /* 鎖 Inner1 實例 */ }
}
static class Inner2 {
public synchronized void method1() { /* 鎖 Inner2 實例 */ }
}
}
// Inner1.method1(inner2) 與 Inner2.method1() 互斥(同鎖 inner2)
// Inner1.method2() 與上述兩者不互斥(不同鎖)
同步塊中修改鎖引用會導致後續線程獲取新鎖,破壞互斥:
private String lock = "123";
public void flawed() {
synchronized(lock) { // 線程 A 獲取 "123" 鎖
lock = "456"; // 修改引用!
sleep(2000);
}
}
// 線程 B 可能獲取 "456" 鎖,與 A 並發執行!
// 推薦 1:final 引用指向不可變對象
private final Object lock = new Object();
private final byte[] lockBytes = new byte[0]; // 零開銷鎖對象
// 推薦 2:業務對象作為鎖(生命週期固定)
public void transfer(Account from, Account to) {
Account first = from.hashCode() < to.hashCode() ? from : to; // 固定獲取順序防死鎖
Account second = first == from ? to : from;
synchronized(first) {
synchronized(second) { /* 轉賬邏輯 */ }
}
}
synchronized(lock) 代碼塊,縮小鎖範圍final,不可變,不可為基本類型包裝類Class.class,實例方法鎖 this,兩者隔離ReentrantLock、LongAdder、ConcurrentHashMap 替代