Lock接口相比synchronized优势

wen java案例 1

本文目录导读:

Lock接口相比synchronized优势

  1. 尝试非阻塞获取锁
  2. 可中断的锁获取
  3. 支持超时
  4. 更灵活的锁释放(手动控制)
  5. 支持多个条件变量(Condition)
  6. 支持公平锁
  7. 提供读写锁(ReadWriteLock)
  8. 总结对比表格
  9. 什么时候用哪个?

Lock 接口相比于 synchronized 关键字,提供了更灵活、更丰富的锁机制,主要优势体现在以下几个方面:

尝试非阻塞获取锁

  • synchronized:如果锁被其他线程持有,当前线程会一直阻塞,直到获得锁,无法中断这个等待过程。
  • Lock:提供了 tryLock() 方法,可以尝试获取锁,如果锁不可用,立即返回 false,线程可以去做其他事情,而不会阻塞等待。
    Lock lock = new ReentrantLock();
    if (lock.tryLock()) {
        try {
            // 临界区
        } finally {
            lock.unlock();
        }
    } else {
        // 锁不可用,执行其他逻辑
    }

可中断的锁获取

  • synchronized:一旦线程进入阻塞状态等待锁,无法响应中断(interrupt()),除非线程获得了锁,否则无法退出等待。
  • Lock:提供了 lockInterruptibly() 方法,在等待锁的过程中,如果其他线程中断了当前线程,它会抛出 InterruptedException 并释放等待,这对于需要处理长时间等待或可取消任务的场景非常有用。

支持超时

  • synchronized:没有超时机制,可能会因为死锁或长时间持有锁而导致线程无限期阻塞。
  • Lock:提供了 tryLock(long time, TimeUnit unit) 方法,线程可以指定一个等待超时时间,如果在指定时间内没拿到锁,就放弃等待,返回 false,这可以有效避免死锁。

更灵活的锁释放(手动控制)

  • synchronized:锁的获取和释放完全由 JVM 自动管理,代码块执行完毕或抛出异常时自动释放,虽然简单,但不够灵活。
  • Lock:需要显式地调用 lock()unlock(),这允许你在不同的代码块或方法中获取和释放锁,甚至可以在一个方法中获取锁,在另一个方法中释放(虽然不推荐,但技术上可行),同时也要求你必须finally 块中释放锁,以避免死锁。

支持多个条件变量(Condition)

  • synchronized:只能依赖于 Objectwait() / notify() / notifyAll() 方法进行线程间协作,且只能有一个等待队列

  • Lock:通过 newCondition() 可以创建多个 Condition 对象,每个 Condition 可以拥有自己的等待集(wait set),这可以实现更精细的线程同步控制,可以有一个“缓冲区满”的条件和一个“缓冲区空”的条件,不同的线程可以在不同条件下等待和唤醒。

    Lock lock = new ReentrantLock();
    Condition notFull = lock.newCondition();
    Condition notEmpty = lock.newCondition();
    // 生产者等待 notFull,消费者等待 notEmpty

支持公平锁

  • synchronized:锁的获取不保证公平性,即等待时间最长的线程不一定最先获得锁,这也可能导致线程“饥饿”(某些线程长时间得不到执行)。
  • LockReentrantLock 的构造函数可以传入 boolean fair 参数。new ReentrantLock(true) 创建一个公平锁,公平锁会按照线程请求锁的顺序(FIFO)来分配锁,从而避免线程饥饿。

提供读写锁(ReadWriteLock)

  • synchronized:无法区分读和写操作,无论读还是写,都会阻塞所有其他线程。
  • Lock:提供了 ReadWriteLock 接口及其实现 ReentrantReadWriteLock,它维护了一对锁:
    • 读锁:多个线程可以同时持有读锁(只要没有写锁)。
    • 写锁:写锁是独占的(其他读或写线程都不能持有)。
    • 这种机制在读多写少的场景下能显著提高并发性能

总结对比表格

特性 synchronized Lock
锁类型 可重入、非公平锁 可重入、公平/非公平锁、读写锁
获取锁方式 阻塞等待,无法中断 阻塞、非阻塞尝试(tryLock)、可中断(lockInterruptibly)、超时
释放锁 自动(结束代码块或异常) 手动,必须在 finally 中调用 unlock()
条件变量 单个(Object.wait/notify) 多个(newCondition)
性能 较少竞争时,JVM 优化后性能接近 高竞争场景下,更灵活,通常性能更好
代码复杂性 简单,无需手动释放 复杂,需手动处理锁获取和释放

什么时候用哪个?

  • 更推荐 synchronized:对于简单的同步需求,代码更简洁、不易出错,且 JVM 本身会对 synchronized 进行持续优化(如锁粗化、锁消除、偏向锁等)。
  • 推荐 Lock:当需要更高级的锁特性时(如超时、可中断、尝试获取、读写锁、多个条件变量、公平锁),这些场景下 Lock 提供的灵活性是 synchronized 无法替代的。

简而言之:Locksynchronized 的“瑞士军刀”版本,功能更强大,但使用时需要更小心(尤其是手动释放锁)。

抱歉,评论功能暂时关闭!