开发者

Java 中的synchronized 与 Lock对比及使用场景解析

目录
  • 前言
  • 一、synchronized关键字详解
    • 1. 基本概念
    • 2. 使用方式
      • (1)修饰实例方法
      • (2)修饰静态方法
      • (3)修饰代码块(推荐)
    • 3. 特性总结
    • 二、Lock接口详解(以ReentrantLock为例)
      • 1. 基本概念
        • 2. 使用方式
          • 3. 核心特性
          • 三、synchronized与Lock的核心区别对比表
            • 四、使用场景对比与建议
              • 五、实战案例分析
                • 案例 1:带超时的锁获取(适用于防止死锁)
                  • 案例 2:使用Condition实现生产者-消费者模型
                  • 六、底层原理简析(进阶)
                    • 1.synchronized的底层实现
                      • 2.ReentrantLock的底层实现
                      • ️ 七、最佳实践与注意事项
                        • 八、总结

                          前言

                          在多线程并编程客栈发编程中,线程安全问题始终是开发者需要重点关注的核心内容之一。Java 提供了多种机制来实现同步控制,其中最常用的两种方式是:

                          • 使用 synchronized 关键字
                          • 使用 java.util.concurrent.locks.Lock 接口(如 ReentrantLock

                          虽然两者都能实现线程同步功能,但它们在使用方式、灵活性、可扩展性以及性能优化方面存在显著差异。

                          本文将从底层原理、语法结构、使用场景、优缺点、最佳实践等多个维度对 synchronizedLock 进行全面深入的解析,并通过大量代码示例帮助你更好地理解它们之间的区别与联系。

                          一、synchronized关键字详解

                          1. 基本概念

                          synchronized 是 Java 内置的关键字,用于保证多个线程对共享资源访问时的互斥性和可见性。它可以修饰方法或代码块,确保同一时刻只有一个线程可以执行被同步的代码。

                          2. 使用方式

                          (1)修饰实例方法

                          public synchronized void method() {
                              // 同步整个方法体
                          }
                          

                          此时锁对象是当前类的实例(即 this)。

                          (2)修饰静态方法

                          public static synchronized void staticMethod() {
                              // 同步静态方法
                          }
                          

                          此时锁对象是当前类的 Class 对象(即 ClassName.class)。

                          (3)修饰代码块(推荐)

                          public void method() {
                              synchronized (this) {
                                  // 同步代码块
                              }
                          }
                          

                          更灵活,可以指定任意对象作为锁,推荐使用这种方式以减少锁定范围。

                          3. 特性总结

                          特性描述
                          自动释放锁JVM 在同步块执行结束后自动释放锁
                          不可中断等待获取锁的线程无法被中断
                          非公平锁多个线程竞争时,不保证先等待的线程优先获得锁
                          可重入性支持同一个线程多次获取同一把锁

                          二、Lock接口详解(以ReentrantLock为例)

                          1. 基本概念

                          Lock 是 Java 5 引入的一个接口,位于 java.util.concurrent.locks 包下。常见的实现类有:

                          • ReentrantLock:可重入锁
                          • ReadwriteLock:读写分离锁(实现类为 ReentrantReadWriteLock

                          相比 synchronizedLock 更加灵活和强大,提供了更多高级功能。

                          2. 使用方式

                          Lock lock = new ReentrantLock();
                          lock.lock(); // 手动加锁
                          try {
                              // 临界区逻辑
                          } finally {
                              lock.unlock(); // 必须放在 finally 块中释放锁
                          }
                          

                          ⚠️ 注意:必须手动调用 unlock(),否则可能导致死锁!

                          3. 核心特性

                          特性描述
                          手动管理锁需要显式调用 lock()unlock()
                          可中断等待支持线程在等待锁的过程中响应中断(lockInterruptibly()
                          超时获取锁支持尝试获取锁并设置超时时间(tryLoNseAjkenEUck(long time, TimeUnit unit)
                          公平锁/非公平锁构造函数可选择是否启用公平锁
                          条件变量支持提供 Condition 接口,实现更细粒度的线程通信

                          三、synchronized与Lock的核心区别对比表

                          功能synchronizedLock
                          加锁方式自动加锁、解锁手动加锁、解锁
                          锁类型非公平锁可选公平/非公平
                          可中断❌ 不支持✅ 支持
                          超时机制❌ 不支持✅ 支持
                          尝试获取锁❌ 不支持✅ 支持
                          条件变量❌ 不支持✅ 支持
                          性能优化JDK 1.6+ 已优化更适合高并发场景
                          适用场景简单同步需求复杂并发控制场景

                          四、使用场景对比与建议

                          场景推荐使用说明
                          简单方法或代码块同步synchronized实现简单,无需手动释放锁
                          高并发、复杂同步控制Lock提供更多控制选项,如公平锁、尝试锁等
                          需要线程中断响应Locksynchronized 不支持中断等待
                          需要条件变量配合LockCondition 可替代传统的 wait/notify
                          需要超时获取锁LocktryLock() 方法非常实用

                          五、实战案例分析

                          案例 1:带超时的锁获取(适用于防止死锁)

                          Lock lock = new ReentrantLock();
                          boolean isLocked = false;
                          try {
                              isLocked = lock.tryLock(3, TimeUnit.SECONDS);
                              if (isLocked) {
                                  try {
                                      // 执行业务逻辑
                                  } finally {
                                      lock.unlock();
                                  }
                              } else {
                                  System.out.println("未能在3秒内获取到锁");
                              }
                          } catch (InterruptedException e) {
                              Thread.currentThread().interrupt();
                              System.out.println("线程被中断");
                          }

                          案例 2:使用Condition实现生产者-消费者模型

                          class BoundedQueue {
                              private final Lock lock = new ReentrantLock();
                              private final Condition notFull = lock.newCondition();
                              private final Condition编程客栈 notEmpty = lock.newCondition();
                              private final Queue<Integer> queue = new LinkedList<>();
                              private final int capacity;
                              public BoundedQueue(int capacity) {
                                  this.capacity = capacity;
                              }
                              public void put(int value) throws InterruptedException {
                                  lock.lock();
                                  try {
                                      while (queue.size() == capacity) {
                                          notFull.await(); // 等待队列不满
                                      }
                                      queue.add(value);
                                      notEmpty.signal(); // 唤醒消费者
                                  } finally {
                                      lock.unlock();
                                  }
                              }
                              public int take() throws InterruptedException {
                                  lock.lock();
                                  try {
                                      while (queue.isEmpty()) {
                                          notEmpty.await(); // 等待队列不空
                                      }
                                      retu编程客栈rn queue.poll();
                                  } finally {
                                      lock.unlock();
                                  }
                              }
                          }

                          六、底层原理简析(进阶)

                          1.synchronized的底层实现

                          在 JVM 层面,synchronized 是基于 Monitor(监视器)机制实现的。每个 Java 对象都关联一个 Monitor,当线程进入同步块时,会尝试获取该对象的 Monitor,成功则进入,失败则阻塞。

                          JVM 对其进行了多项优化,包括:

                          • 偏向锁(Biased Locking)
                          • 轻量级锁(Lightweight Locking)
                          • 自旋锁(Spin Lock)
                          • 锁粗化(Lock Coarsening)
                          • 锁消除(Lock Elimination)

                          这些优化使得 synchronized 在现代 JVM 上表现优异。

                          2.ReentrantLock的底层实现

                          ReentrantLock 底层依赖于 AbstractQueuedSynchronizer(AQS)框架,是一个基于 CLH(Craig, Landin, and Hagersten)队列的同步工具。

                          它通过 CAS(Compare and Swap)操作和 volatile 变量实现线程安全,具有更高的可控性和灵活性。

                          ️ 七、最佳实践与注意事项

                          建议说明
                          优先考虑 synchronized如果只是简单的同步,优先使用 synchronized,避免复杂代码
                          Lock 放在 finally 中释放防止因异常导致死锁
                          使用 tryLock() 防止死锁在某些情况下,尝试获取锁比无限等待更合理
                          避免嵌套锁容易引发死锁,应尽量避www.devze.com免或使用工具检测
                          选择公平锁需谨慎公平锁虽然保证顺序,但可能带来性能损耗
                          使用 Condition 替代 wait/notify更清晰、线程安全

                          八、总结

                          项目synchronizedLock
                          是否内置✅ 是❌ 否
                          使用难度简单复杂
                          控制粒度
                          功能丰富度一般强大
                          性能表现更好(高并发)
                          推荐用途初学者、简单同步高级用户、复杂并发控制

                          在实际开发中,两者各有优势,选择哪一个取决于具体的应用场景和团队技术栈。对于大多数中小型项目,synchronized 已经足够;而在需要更高并发控制能力的场景下,Lock 更具优势。

                          到此这篇关于Java 中的 synchronized 与 Lock:深度对比、使用场景及高级用法的文章就介绍到这了,更多相关Java synchronized 与 Lock内容请搜索编程客栈(www.devze.com)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程客栈(www.devze.com)!

                          0

                          上一篇:

                          下一篇:

                          精彩评论

                          暂无评论...
                          验证码 换一张
                          取 消

                          最新开发

                          开发排行榜