首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >Java并发编程:深入对比synchronized与ReentrantLock

Java并发编程:深入对比synchronized与ReentrantLock

作者头像
用户8589624
发布2025-11-16 09:40:53
发布2025-11-16 09:40:53
1260
举报
文章被收录于专栏:nginxnginx

Java并发编程:深入对比synchronized与ReentrantLock

引言

在Java多线程编程中,同步机制是确保线程安全的核心。synchronizedReentrantLock是Java中最常用的两种同步工具,它们各有特点,适用于不同的场景。本文将深入分析这两种同步机制的实现原理、使用方式、性能差异以及适用场景,帮助开发者做出更明智的选择。

1. 基本概念对比

1.1 synchronized关键字

synchronized是Java内置的同步机制,它提供了一种简单的方式来实现线程同步:

代码语言:javascript
复制
public class SynchronizedExample {
    private int count = 0;
    
    // 同步方法
    public synchronized void increment() {
        count++;
    }
    
    public void incrementBlock() {
        // 同步代码块
        synchronized(this) {
            count++;
        }
    }
}
1.2 ReentrantLock类

ReentrantLock是Java 5引入的显式锁机制,位于java.util.concurrent.locks包中:

代码语言:javascript
复制
import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockExample {
    private int count = 0;
    private final ReentrantLock lock = new ReentrantLock();
    
    public void increment() {
        lock.lock();  // 获取锁
        try {
            count++;
        } finally {
            lock.unlock();  // 释放锁
        }
    }
}

2. 特性对比

2.1 可重入性

两者都是可重入的,即同一个线程可以多次获取同一个锁:

代码语言:javascript
复制
// synchronized的可重入性
public synchronized void methodA() {
    methodB();  // 可以调用另一个同步方法
}

public synchronized void methodB() {
    // ...
}

// ReentrantLock的可重入性
public void methodA() {
    lock.lock();
    try {
        methodB();
    } finally {
        lock.unlock();
    }
}

public void methodB() {
    lock.lock();
    try {
        // ...
    } finally {
        lock.unlock();
    }
}
2.2 公平性

synchronized不提供公平性保证,而ReentrantLock可以配置为公平锁:

代码语言:javascript
复制
// 创建公平锁
ReentrantLock fairLock = new ReentrantLock(true);

公平锁会按照线程请求锁的顺序来分配锁,但会降低吞吐量。

2.3 锁的获取方式
  • synchronized:自动获取和释放锁,进入同步代码块时获取,退出时释放
  • ReentrantLock:需要显式调用lock()unlock()方法
2.4 中断响应

ReentrantLock可以响应中断,而synchronized不能:

代码语言:javascript
复制
public void interruptibleLock() throws InterruptedException {
    ReentrantLock lock = new ReentrantLock();
    try {
        lock.lockInterruptibly();  // 可中断的获取锁
        try {
            // 临界区代码
        } finally {
            lock.unlock();
        }
    } catch (InterruptedException e) {
        // 处理中断
    }
}
2.5 条件变量

ReentrantLock可以创建多个Condition对象,而synchronized只能有一个等待队列:

代码语言:javascript
复制
public class ConditionExample {
    private final ReentrantLock lock = new ReentrantLock();
    private final Condition notEmpty = lock.newCondition();
    private final Condition notFull = lock.newCondition();
    private final Object[] items = new Object[100];
    private int count;
    
    public void put(Object x) throws InterruptedException {
        lock.lock();
        try {
            while (count == items.length)
                notFull.await();  // 等待不满条件
            items[count++] = x;
            notEmpty.signal();  // 唤醒等待不空条件的线程
        } finally {
            lock.unlock();
        }
    }
    
    public Object take() throws InterruptedException {
        lock.lock();
        try {
            while (count == 0)
                notEmpty.await();  // 等待不空条件
            Object x = items[--count];
            notFull.signal();  // 唤醒等待不满条件的线程
            return x;
        } finally {
            lock.unlock();
        }
    }
}

3. 性能对比

在Java早期版本中,ReentrantLock的性能明显优于synchronized。但随着Java版本的更新,synchronized的性能得到了显著提升,现在两者的性能差异已经不大。

在低竞争情况下,synchronized可能更优,因为它是JVM内置特性;在高竞争情况下,ReentrantLock的可配置性可能带来更好的性能。

4. 使用场景

4.1 优先使用synchronized的情况
  • 简单的同步需求
  • 不需要高级特性(如可中断、公平锁、多个条件变量)
  • 代码简洁性更重要时
4.2 优先使用ReentrantLock的情况
  • 需要可中断的锁获取操作
  • 需要公平锁
  • 需要多个条件变量
  • 需要尝试获取锁(tryLock)
代码语言:javascript
复制
public void tryLockExample() {
    ReentrantLock lock = new ReentrantLock();
    if (lock.tryLock()) {
        try {
            // 获取锁成功,执行临界区代码
        } finally {
            lock.unlock();
        }
    } else {
        // 获取锁失败,执行其他操作
    }
}

5. 最佳实践

5.1 synchronized最佳实践
  • 尽量缩小同步代码块的范围
  • 避免在同步代码块中执行耗时操作
  • 注意锁的粒度,避免不必要的同步
5.2 ReentrantLock最佳实践
  • 总是在finally块中释放锁
  • 考虑使用tryLock避免死锁
  • 合理使用Condition实现精细的线程通信
代码语言:javascript
复制
public void transfer(Account from, Account to, int amount) {
    // 按照固定顺序获取锁,避免死锁
    int fromHash = System.identityHashCode(from);
    int toHash = System.identityHashCode(to);
    
    if (fromHash < toHash) {
        from.lock.lock();
        to.lock.lock();
    } else if (fromHash > toHash) {
        to.lock.lock();
        from.lock.lock();
    } else {
        // 哈希冲突时的处理
        synchronized (from) {
            synchronized (to) {
                // 转账操作
            }
        }
        return;
    }
    
    try {
        // 执行转账操作
    } finally {
        from.lock.unlock();
        to.lock.unlock();
    }
}

6. 底层实现

6.1 synchronized实现原理

synchronized是基于JVM层面的Monitor实现的,包含以下概念:

  • 进入区(Entry Set)
  • 拥有区(Owner)
  • 等待区(Wait Set)

在字节码层面,同步方法通过ACC_SYNCHRONIZED标志实现,同步代码块通过monitorentermonitorexit指令实现。

6.2 ReentrantLock实现原理

ReentrantLock是基于AQS(AbstractQueuedSynchronizer)实现的,主要包含:

  • state变量表示锁的状态
  • CLH队列维护等待线程
  • CAS操作保证原子性

7. 选择建议

在选择synchronizedReentrantLock时,可以考虑以下因素:

  1. 复杂性:简单场景用synchronized,复杂场景用ReentrantLock
  2. 性能:现代JVM中两者性能接近,除非有特殊需求
  3. 功能需求:需要高级功能时选择ReentrantLock
  4. 可维护性synchronized代码更简洁,不易出错
  5. 可扩展性:未来可能需要更多功能时选择ReentrantLock

8. 总结

synchronizedReentrantLock都是Java中强大的同步工具,各有优缺点:

特性

synchronized

ReentrantLock

实现方式

JVM内置

JDK实现

使用复杂度

简单

较复杂

可重入性

支持

支持

公平性

不支持

可配置

可中断性

不支持

支持

尝试获取锁

不支持

支持(tryLock)

条件变量

单一

多个

性能

现代JVM优化好

高竞争下可能更优

异常时自动释放锁

支持

需手动释放

在实际开发中,应根据具体需求选择合适的同步机制。对于大多数简单场景,synchronized是更简洁安全的选择;而对于需要更高级功能的复杂场景,ReentrantLock提供了更大的灵活性。

理解这两种同步机制的原理和特性,有助于我们编写出更高效、更安全的并发程序。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-11-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Java并发编程:深入对比synchronized与ReentrantLock
    • 引言
    • 1. 基本概念对比
      • 1.1 synchronized关键字
      • 1.2 ReentrantLock类
    • 2. 特性对比
      • 2.1 可重入性
      • 2.2 公平性
      • 2.3 锁的获取方式
      • 2.4 中断响应
      • 2.5 条件变量
    • 3. 性能对比
    • 4. 使用场景
      • 4.1 优先使用synchronized的情况
      • 4.2 优先使用ReentrantLock的情况
    • 5. 最佳实践
      • 5.1 synchronized最佳实践
      • 5.2 ReentrantLock最佳实践
    • 6. 底层实现
      • 6.1 synchronized实现原理
      • 6.2 ReentrantLock实现原理
    • 7. 选择建议
    • 8. 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档