synchronize 或者lock 锁常见的使用场景

在 Java 多线程编程中,synchronizedLock(如 ReentrantLock)是两种常见的线程同步机制。以下是它们的核心区别和典型使用场景,结合代码示例说明:


一、synchronized 的常见场景

1. 简单的临界区保护
public class Counter {
    private int count = 0;
    
    // synchronized 修饰方法(锁对象为当前实例)
    public synchronized void increment() {
        count++;
    }
    
    // synchronized 代码块(显式指定锁对象)
    public void decrement() {
        synchronized(this) {
            count--;
        }
    }
}

适用场景

  • 快速实现线程安全的单例模式、计数器等简单同步逻辑
  • 不需要手动释放锁,避免锁泄漏风险
2. 类级别锁
public class Logger {
    // 锁对象为 Class 对象
    public static synchronized void log(String message) {
        System.out.println(Thread.currentThread().getName() + ": " + message);
    }
}

适用场景

  • 需要全局唯一的锁(如静态方法同步)
3. 对象内部状态保护
public class BankAccount {
    private double balance;
    
    public synchronized void transfer(BankAccount target, double amount) {
        if (this.balance >= amount) {
            this.balance -= amount;
            target.balance += amount;
        }
    }
}

适用场景

  • 保护对象内部状态的原子性操作(如账户转账)

二、Lock 的常见场景

1. 非阻塞尝试获取锁
public class Cache {
    private final ReentrantLock lock = new ReentrantLock();
    private Map<String, Object> data = new HashMap<>();

    public boolean tryUpdate(String key, Object value) {
        if (lock.tryLock()) {  // 尝试获取锁,失败立即返回
            try {
                data.put(key, value);
                return true;
            } finally {
                lock.unlock();
            }
        }
        return false;
    }
}

适用场景

  • 高并发下避免线程阻塞(如缓存更新、限流逻辑)
2. 可中断的锁等待
public class TaskQueue {
    private final ReentrantLock lock = new ReentrantLock();
    private Queue<Runnable> queue = new LinkedList<>();

    public Runnable getTask() throws InterruptedException {
        lock.lockInterruptibly();  // 允许被中断的锁等待
        try {
            while (queue.isEmpty()) {
                Condition notEmpty = lock.newCondition();
                notEmpty.await();
            }
            return queue.poll();
        } finally {
            lock.unlock();
        }
    }
}

适用场景

  • 需要响应中断的任务调度(如线程池任务队列)
3. 公平锁
public class TicketSystem {
    // 公平锁(按请求顺序分配锁)
    private final ReentrantLock lock = new ReentrantLock(true); 
    private int tickets = 100;

    public void buyTicket() {
        lock.lock();
        try {
            if (tickets > 0) {
                System.out.println(Thread.currentThread().getName() + " buys ticket " + tickets--);
            }
        } finally {
            lock.unlock();
        }
    }
}

适用场景

  • 需要避免线程饥饿(如票务系统、资源按顺序分配)
4. 读写锁分离
public class ConfigManager {
    private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock private Map<String, String> config = new HashMap<>();

    // 读操作(共享锁)
    public String getConfig(String key) {
        rwLock.readLock().lock();
        try {
            return config.get(key);
        } finally {
            rwLock.readLock().unlock();
        }
    }

    // 写操作(独占锁)
    public void updateConfig(String key, String value) {
        rwLock.writeLock().lock();
        try {
            config.put(key, value);
        } finally {
            rwLock.writeLock().unlock();
        }
    }
}

适用场景

  • 读多写少的场景(如配置管理、缓存系统)

三、如何选择?

特性synchronizedLock
锁释放自动释放(代码块/方法退出时)必须手动调用 unlock()
可中断性不支持支持 (lockInterruptibly())
超时机制不支持支持 (tryLock(long, TimeUnit))
公平锁非公平锁(默认)可配置公平锁
锁绑定多个条件不支持支持 (newCondition())
性能JDK6 后优化较好(偏向锁/轻量级锁)高竞争场景下性能更优

四、高频面试点

  1. 锁升级过程(偏向锁 → 轻量级锁 → 重量级锁)
  2. synchronized 的底层实现(对象头 Mark Word、Monitor 机制)
  3. AQS(AbstractQueuedSynchronizer)原理(Lock 的底层实现)
  4. 死锁避免方法(如按固定顺序获取锁、使用 tryLock
  5. 锁粒度优化(如缩小同步代码块范围)

五、陷阱提醒

  • ❌ 在循环中调用 tryLock() 时未设置超时,可能导致 CPU 空转
  • ❌ 忘记在 finally 块中释放 Lock,造成锁泄漏
  • ❌ 错误使用 synchronized 修饰静态方法和实例方法,导致锁对象混淆

掌握这些核心场景和底层原理,可以更好地回答面试中的锁相关问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值