You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java中ReentrantLock与synchronized的区别及适用场景

Synchronized vs ReentrantLock: 差异、适用场景与性能分析

核心差异

两者都是Java中实现线程同步的核心手段,但在功能特性和使用方式上存在明显区别:

  • 语法与锁释放机制:
    • synchronized是Java关键字,无需手动管理锁释放——线程退出同步代码块/方法时,JVM会自动释放锁,即使发生异常也不会遗漏。
    • ReentrantLock是java.util.concurrent.locks包下的类,必须手动调用lock()获取锁,且必须在finally块中执行unlock()释放锁,否则会导致锁泄漏。
  • 锁获取灵活性:
    • ReentrantLock支持多种灵活的锁获取方式:
      • tryLock():非阻塞式获取锁,成功返回true,失败直接返回false,不会阻塞线程。
      • tryLock(long timeout, TimeUnit unit):指定超时时间获取锁,超时未获取则返回false。
      • lockInterruptibly():允许在等待锁的过程中响应线程中断,避免线程无限期阻塞。
    • synchronized不支持上述特性,线程进入锁等待状态后无法被中断,只能等待锁释放或JVM终止。
  • 公平性支持:
    • ReentrantLock构造时可指定公平模式(new ReentrantLock(true)),公平锁会按线程请求锁的顺序分配锁,避免饥饿问题。
    • synchronized仅支持非公平锁,JVM会随机唤醒等待队列中的线程,可能导致部分线程长期无法获取锁。
  • 条件队列支持:
    • ReentrantLock可通过newCondition()创建多个独立的Condition对象,每个Condition对应专属的等待/通知队列,能实现更精细的线程协作(比如区分“队列空”和“队列满”的不同等待场景)。
    • synchronized只能依赖对象本身的单一等待队列,通过wait()/notify()/notifyAll()实现通知,notifyAll()会唤醒所有等待线程,效率较低。

适用场景选择

  • 优先使用ynchronized的场景:
    • 简单的同步需求,比如单个方法或小型代码块的同步。
    • 不需要额外锁功能的场景,synchronized语法简洁,无需手动管理锁释放,出错概率更低。
    • 大多数日常业务场景,JVM对ynchronized的优化已经足够成熟,性能表现优异。
  • 优先使用ReentrantLock的场景:
    • 需要中断等待锁的线程:比如线程在等待锁时需要响应中断信号,避免无限期阻塞。
    • 需要尝试非阻塞获取锁:比如希望在获取锁失败时执行其他逻辑,而非阻塞等待。
    • 需要公平锁:比如必须保证线程按请求顺序获取锁,避免饥饿问题。
    • 需要多条件队列的线程协作:比如生产者消费者模型中,需要分别处理队列空和队列满的等待逻辑。

性能影响

  • JDK版本差异:
    • JDK 1.6之前,synchronized是重量级锁,性能远低于ReentrantLock。
    • JDK 1.6之后,JVM引入了偏向锁、轻量级锁、自旋锁等一系列优化,synchronized的性能大幅提升,与ReentrantLock在大部分场景下差异极小。
  • 极端场景表现:
    • 在高并发且锁竞争非常激烈的场景下,ReentrantLock可能略占优势,但这种场景在日常开发中并不常见。
    • 需注意:ReentrantLock如果忘记在finally中释放锁,会导致锁泄漏,引发性能下降甚至死锁,这是比性能差异更严重的问题。

代码示例

1. Synchronized实现计数器

public class SyncCounter {
    private int count = 0;

    public void increment() {
        // 同步代码块,锁对象为当前实例
        synchronized (this) {
            count++;
        }
    }

    public int getCount() {
        synchronized (this) {
            return count;
        }
    }
}

2. ReentrantLock实现可尝试获取锁的计数器

import java.util.concurrent.locks.ReentrantLock;

public class LockCounter {
    private int count = 0;
    private final ReentrantLock lock = new ReentrantLock();

    public void increment() {
        lock.lock();
        try {
            count++;
        } finally {
            // 必须在finally中释放锁,避免异常导致锁泄漏
            lock.unlock();
        }
    }

    // 尝试非阻塞获取锁,获取失败直接返回
    public boolean tryIncrement() {
        if (lock.tryLock()) {
            try {
                count++;
                return true;
            } finally {
                lock.unlock();
            }
        }
        return false;
    }

    public int getCount() {
        lock.lock();
        try {
            return count;
        } finally {
            lock.unlock();
        }
    }
}

3. ReentrantLock多条件队列实现有界队列

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class BoundedQueue<T> {
    private final T[] items;
    private int head, tail, count;
    private final ReentrantLock lock = new ReentrantLock();
    // 两个独立的条件队列:队列非空、队列非满
    private final Condition notEmpty = lock.newCondition();
    private final Condition notFull = lock.newCondition();

    @SuppressWarnings("unchecked")
    public BoundedQueue(int capacity) {
        items = (T[]) new Object[capacity];
    }

    public void put(T item) throws InterruptedException {
        // 支持中断的锁获取
        lock.lockInterruptibly();
        try {
            // 队列满时,等待非满条件
            while (count == items.length) {
                notFull.await();
            }
            items[tail] = item;
            tail = (tail + 1) % items.length;
            count++;
            // 通知等待非空条件的线程
            notEmpty.signal();
        } finally {
            lock.unlock();
        }
    }

    public T take() throws InterruptedException {
        lock.lockInterruptibly();
        try {
            // 队列空时,等待非空条件
            while (count == 0) {
                notEmpty.await();
            }
            T item = items[head];
            head = (head + 1) % items.length;
            count--;
            // 通知等待非满条件的线程
            notFull.signal();
            return item;
        } finally {
            lock.unlock();
        }
    }
}

内容的提问来源于stack exchange,提问作者Temiloluwa

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 11:12:28