基本概念
synchronized(this) 是 Java 中的一种同步机制,通过对当前对象实例加锁来保证线程安全。当一个线程进入被 synchronized(this) 修饰的代码块时,会获取当前对象的锁,其他线程必须等待该线程释放锁后才能进入。
基本语法
public class MyClass {public void method() {synchronized(this) {// 需要同步保护的代码// 同一时刻只有一个线程能执行这里的代码}}
}
使用场景详解
1. 实例变量的线程安全操作
public class Counter {private int count = 0;// 不安全的递增方法public void unsafeIncrement() {count++; // 非原子操作,可能出现竞态条件}// 安全的递增方法public void safeIncrement() {synchronized(this) {count++;}}// 获取当前计数值public int getCount() {synchronized(this) {return count;}}
}
2. 复杂业务逻辑的原子性保证
public class BankAccount {private double balance;public BankAccount(double initialBalance) {this.balance = initialBalance;}public boolean withdraw(double amount) {synchronized(this) {// 检查余额是否充足if (balance >= amount) {// 模拟处理时间try {Thread.sleep(100);} catch (InterruptedException e) {Thread.currentThread().interrupt();}balance -= amount;return true;}return false;}}public void deposit(double amount) {synchronized(this) {// 模拟处理时间try {Thread.sleep(50);} catch (InterruptedException e) {Thread.currentThread().interrupt();}balance += amount;}}public double getBalance() {synchronized(this) {return balance;}}
}
3. 多步骤操作的原子性保证
public class InventoryManager {private Map<String, Integer> inventory = new HashMap<>();public InventoryManager() {// 初始化库存inventory.put("item1", 100);inventory.put("item2", 50);}// 批量扣减库存 - 需要保证原子性public boolean deductInventory(Map<String, Integer> itemsToDeduct) {synchronized(this) {// 第一步:检查所有商品库存是否充足for (Map.Entry<String, Integer> entry : itemsToDeduct.entrySet()) {String itemId = entry.getKey();Integer quantity = entry.getValue();Integer currentStock = inventory.get(itemId);if (currentStock == null || currentStock < quantity) {return false; // 库存不足,整个操作失败}}// 第二步:扣减所有商品库存for (Map.Entry<String, Integer> entry : itemsToDeduct.entrySet()) {String itemId = entry.getKey();Integer quantity = entry.getValue();Integer currentStock = inventory.get(itemId);inventory.put(itemId, currentStock - quantity);}return true;}}public int getStock(String itemId) {synchronized(this) {return inventory.getOrDefault(itemId, 0);}}
}
4. 缓存更新的线程安全
public class CachedDataService {private Map<String, Object> cache = new HashMap<>();private long lastUpdateTime = 0;private static final long CACHE_TIMEOUT = 60000; // 1分钟public Object getData(String key) {synchronized(this) {// 检查缓存是否过期if (System.currentTimeMillis() - lastUpdateTime > CACHE_TIMEOUT) {refreshCache();}return cache.get(key);}}public void putData(String key, Object data) {synchronized(this) {cache.put(key, data);lastUpdateTime = System.currentTimeMillis();}}private void refreshCache() {// 刷新缓存逻辑cache.clear();// 重新加载数据...lastUpdateTime = System.currentTimeMillis();}public void clearCache() {synchronized(this) {cache.clear();lastUpdateTime = 0;}}
}
与其他同步方式的比较
1. synchronized 方法 vs synchronized(this)
public class ComparisonExample {private int value = 0;// 方式1:同步整个方法public synchronized void method1() {value++;// 其他操作...}// 方式2:同步代码块public void method2() {// 一些不需要同步的操作System.out.println("不需要同步的操作");synchronized(this) {value++;// 需要同步的操作}// 更多不需要同步的操作System.out.println("更多不需要同步的操作");}
}
2. synchronized(this) vs synchronized(object)
public class LockGranularityExample {private final Object lock1 = new Object();private final Object lock2 = new Object();private int counter1 = 0;private int counter2 = 0;// 粗粒度锁定 - 锁定整个对象public void coarseGrainedOperation() {synchronized(this) {counter1++;counter2++;}}// 细粒度锁定 - 分别锁定不同的资源public void fineGrainedOperation() {synchronized(lock1) {counter1++;}synchronized(lock2) {counter2++;}}
}
实际应用场景
1. 单例模式中的懒加载
public class LazySingleton {private static LazySingleton instance;private LazySingleton() {}public static LazySingleton getInstance() {if (instance == null) {synchronized(LazySingleton.class) {if (instance == null) {instance = new LazySingleton();}}}return instance;}
}
2. 生产者-消费者模式
public class Buffer {private final Queue<Integer> queue = new LinkedList<>();private final int capacity = 10;public void produce(int item) throws InterruptedException {synchronized(this) {while (queue.size() == capacity) {// 缓冲区满,等待消费者消费wait();}queue.offer(item);System.out.println("生产: " + item);notifyAll(); // 通知等待的消费者}}public int consume() throws InterruptedException {synchronized(this) {while (queue.isEmpty()) {// 缓冲区空,等待生产者生产wait();}int item = queue.poll();System.out.println("消费: " + item);notifyAll(); // 通知等待的生产者return item;}}
}
3. 状态机的状态转换
public class StateMachine {private enum State {IDLE, PROCESSING, COMPLETED, ERROR}private State currentState = State.IDLE;public void startProcessing() {synchronized(this) {if (currentState == State.IDLE) {currentState = State.PROCESSING;System.out.println("开始处理...");} else {throw new IllegalStateException("不能从 " + currentState + " 状态开始处理");}}}public void completeProcessing() {synchronized(this) {if (currentState == State.PROCESSING) {currentState = State.COMPLETED;System.out.println("处理完成");} else {throw new IllegalStateException("不能在 " + currentState + " 状态完成处理");}}}public State getCurrentState() {synchronized(this) {return currentState;}}
}
注意事项和最佳实践
1. 避免死锁
public class DeadlockAvoidance {private final Object lockA = new Object();private final Object lockB = new Object();// 错误的做法 - 可能导致死锁public void badMethod1() {synchronized(lockA) {synchronized(lockB) {// 操作...}}}public void badMethod2() {synchronized(lockB) {synchronized(lockA) {// 操作...}}}// 正确的做法 - 统一加锁顺序public void goodMethod1() {synchronized(lockA) {synchronized(lockB) {// 操作...}}}public void goodMethod2() {synchronized(lockA) { // 保持相同的加锁顺序synchronized(lockB) {// 操作...}}}
}
2. 减少锁的持有时间
public class LockHoldingOptimization {private List<String> dataList = new ArrayList<>();// 不好的做法 - 锁持有时间过长public void badPractice() {synchronized(this) {// 执行耗时操作expensiveOperation();// 添加数据dataList.add("data");}}// 好的做法 - 减少锁持有时间public void goodPractice() {// 先执行耗时操作String result = expensiveOperation();// 只在必要时加锁synchronized(this) {dataList.add(result);}}private String expensiveOperation() {// 模拟耗时操作try {Thread.sleep(1000);} catch (InterruptedException e) {Thread.currentThread().interrupt();}return "processed_data";}
}
总结
synchronized(this) 的主要特点和使用建议:
适用场景:
保护实例变量的并发访问
保证多步骤操作的原子性
实现简单的线程同步
注意事项:
锁的是当前对象实例,同一类的不同实例之间不会互斥
可能影响性能,应尽量减少锁的持有时间
注意避免死锁
替代方案:
对于更复杂的并发控制,可以考虑使用 java.util.concurrent 包中的工具类
对于读多写少的场景,可以考虑使用 ReentrantReadWriteLock
对于无锁编程,可以考虑使用 Atomic 类
synchronized(this) 是 Java 中最基础也是最重要的同步机制之一,正确使用能够有效解决大部分线程安全问题。