synchronized 和 ReentrantLock 区别

1. 基本概念

  • synchronizedsynchronized是Java语言的内置关键字,主要用于对方法或代码块进行同步。它通过对象的内置锁(Monitor)实现线程之间的互斥访问。

  • ReentrantLockReentrantLockjava.util.concurrent.locks包中的一个类,它提供了与synchronized类似的功能,但具有更高的灵活性和扩展性。ReentrantLock是一种显式锁,需要显式地加锁和解锁。

2. 功能和使用方式

2.1. synchronized的使用

synchronized的使用相对简单,可以在方法声明或代码块前加上synchronized关键字。

示例:

public class SynchronizedExample {
    private int count = 0;

    // 同步方法
    public synchronized void increment() {
        count++;
    }

    // 同步代码块
    public void incrementWithBlock() {
        synchronized (this) {
            count++;
        }
    }
}

在上面的例子中,synchronized关键字确保同一时刻只有一个线程能够执行increment方法或同步代码块。

2.2. ReentrantLock的使用

ReentrantLock提供了更灵活的锁机制,需要显式地锁定和解锁,通常通过lock()unlock()方法来实现。

示例:

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

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

    public void increment() {
        lock.lock();  // 显式加锁
        try {
            count++;
        } finally {
            lock.unlock();  // 确保在最终释放锁
        }
    }
}

在这个例子中,ReentrantLock提供了显式的加锁和解锁操作,开发者必须确保每次加锁后都在finally块中释放锁,以避免死锁。

3. 主要区别

3.1. 可重入性
  • synchronizedsynchronized是可重入的,这意味着一个线程在已经获得了某个锁的情况下,可以再次获取该锁而不会被阻塞。例如,当一个Synchronized方法调用另一个同步方法时,线程不会再次阻塞自己。

  • ReentrantLockReentrantLock同样是可重入的。它允许线程多次获取同一个锁,类似于synchronized的行为。

示例:

public class ReentrantExample {
    private final Lock lock = new ReentrantLock();

    public void methodA() {
        lock.lock();
        try {
            methodB();  // 这里不会死锁,因为 ReentrantLock 是可重入的
        } finally {
            lock.unlock();
        }
    }

    public void methodB() {
        lock.lock();
        try {
            // 业务逻辑
        } finally {
            lock.unlock();
        }
    }
}
3.2. 锁的公平性
  • synchronizedsynchronized是非公平锁,锁的获取顺序由JVM和操作系统调度决定,可能导致某些线程长期得不到锁的机会,出现“饥饿”现象。

  • ReentrantLockReentrantLock可以选择公平锁和非公平锁。公平锁按线程的请求顺序(FIFO)获取锁,非公平锁则可能发生“抢锁”现象。默认情况下,ReentrantLock是非公平的,但可以通过构造函数指定为公平锁。

示例:

// 创建一个公平的 ReentrantLock
Lock fairLock = new ReentrantLock(true);

// 创建一个非公平的 ReentrantLock
Lock unfairLock = new ReentrantLock(false);
3.3. 中断响应能力
  • synchronizedsynchronized不支持中断响应。当一个线程被阻塞在synchronized的锁上时,它不能被中断,只能等待锁的释放。

  • ReentrantLockReentrantLock支持中断响应。线程在尝试获取锁时,可以被中断,特别是在调用lockInterruptibly()方法时,这对于响应性要求高的应用非常有用。

示例:

public void interruptibleLock() {
    try {
        lock.lockInterruptibly();
        // 业务逻辑
    } catch (InterruptedException e) {
        // 处理中断
    } finally {
        lock.unlock();
    }
}
3.4. 条件等待
  • synchronizedsynchronized结合Objectwait()notify()方法,可以实现线程间的协调和通信,但这些方法使用相对复杂,并且必须在同步块或方法中调用。

  • ReentrantLockReentrantLock提供了更灵活的条件变量机制,通过Condition接口实现。一个ReentrantLock可以创建多个Condition对象,用于实现复杂的等待/通知机制。

示例:

public class ConditionExample {
    private final Lock lock = new ReentrantLock();
    private final Condition condition = lock.newCondition();

    public void awaitMethod() throws InterruptedException {
        lock.lock();
        try {
            condition.await();  // 当前线程等待
        } finally {
            lock.unlock();
        }
    }

    public void signalMethod() {
        lock.lock();
        try {
            condition.signal();  // 唤醒一个等待线程
        } finally {
            lock.unlock();
        }
    }
}
3.5. 性能与开销
  • synchronizedsynchronized是Java语言级别的内置机制,JVM对其进行了大量优化,尤其是在JDK 1.6以后,通过引入偏向锁、轻量级锁等机制,使得synchronized的性能得到了显著提升。在低竞争场景下,synchronized的性能非常好。

  • ReentrantLockReentrantLock是一个API级别的锁,由于其丰富的特性(如公平锁、可中断、条件等待等),在高竞争场景下可能表现更优,尤其是在需要灵活控制锁行为的复杂场景中。

3.6. 锁降级与锁释放
  • synchronized:当线程执行完synchronized代码块或方法时,锁自动释放。没有手动释放锁的操作,也不能降级锁。

  • ReentrantLock:开发者必须显式地调用unlock()方法释放锁,这提供了更大的灵活性。例如,可以在不同的代码路径下有选择地释放锁。ReentrantLock 还可以在持有锁的同时获取不同级别的锁。

示例:

lock.lock();
// 一些操作
if (conditionMet) {
    lock.unlock();  // 提前释放锁
}
3.7. 监控与调试
  • synchronized:在监控和调试时,synchronized的锁持有信息可以通过线程转储(Thread Dump)直接查看,方便定位死锁和锁竞争问题。

  • ReentrantLock:ReentrantLock 提供了一些额外的方法用于获取锁的信息,比如getHoldCount()isLocked(),这在调试和监控中更为方便。

4. 适用场景

  • synchronized:适用于简单的同步场景,尤其是在同步块相对较小、锁竞争较少的情况下。其使用简洁,语法直观,并且由JVM自动管理锁的释放。

  • ReentrantLock:适用于复杂的同步场景,特别是需要公平锁、可中断锁、条件等待、显式锁管理等功能时。由于它的灵活性,ReentrantLock在高并发、复杂逻辑的场景下表现更好。

5. 总结

synchronizedReentrantLock各有优缺点:

  • synchronized:内置锁,使用简单,自动管理锁的获取和释放,JVM对其进行了优化,适用于大多数的同步场景。在低竞争的场景中,它的性能表现非常好。

  • ReentrantLock:提供了更高的灵活性和更多的功能,如公平锁、可中断锁、条件变量等。需要手动管理锁的获取和释放,适用于高并发和复杂的同步需求。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Flying_Fish_Xuan

你的鼓励将是我创作最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值