【JAVA-面经】4.Synchronized 和 ReentrantLock的区别

SynchronizedReentrantLock 都是 Java 中用于实现线程同步的机制,它们在功能上有相似之处,但也有一些关键的区别。以下是它们的主要区别:

1. 底层实现机制

  • Synchronized:是 Java 的内置关键字,JVM 直接支持。Synchronized 是一种 非公平锁,使用的是重量级的操作,主要通过对象的监视器锁(Monitor)来实现,依赖于 JVM 层面。
  • ReentrantLock:是 Java java.util.concurrent.locks 包中的类,它是基于Java 代码实现的可重入锁,提供了更灵活的锁机制。它属于显式锁,即开发者需要手动加锁和解锁。

2. 公平锁与非公平锁

  • Synchronized:只能是 非公平锁,即无法保证线程获取锁的顺序。线程争抢锁时,不能保证最早等待的线程一定能优先获取到锁。
  • ReentrantLock:可以选择是 公平锁非公平锁。通过构造函数 ReentrantLock(boolean fair),可以指定是否创建公平锁。如果使用公平锁,等待时间最长的线程优先获取锁。

3. 锁的获取与释放

  • Synchronized:锁的获取与释放是隐式的。线程进入 synchronized 块或方法时自动获取锁,退出时自动释放锁,即便线程异常退出也能保证锁会被释放。
  • ReentrantLock:锁的获取与释放是显式的,必须使用 lock() 手动加锁,使用 unlock() 手动解锁。如果在 try-finally 代码块中没有正确释放锁,可能导致死锁问题,因此通常建议在 finally 块中释放锁。

示例:

ReentrantLock lock = new ReentrantLock();
try {
    lock.lock();  // 显式加锁
    // 线程安全的代码块
} finally {
    lock.unlock();  // 显式解锁
}

4. 中断响应

  • Synchronized:不支持中断锁等待。如果一个线程在等待锁时被阻塞,它无法响应中断信号。
  • ReentrantLock:支持中断。通过 lockInterruptibly() 方法,线程在等待获取锁时可以响应中断,从而避免长时间等待或死锁的情况。

示例:

try {
    lock.lockInterruptibly();  // 支持中断的锁
} catch (InterruptedException e) {
    // 响应中断
}

5. 尝试获取锁

  • Synchronized:不支持尝试获取锁,线程要么成功获取锁,要么一直等待直到锁释放。
  • ReentrantLock:可以使用 tryLock() 方法尝试获取锁,立即返回锁是否成功获取。如果未成功获取锁,线程不会阻塞,还可以设定超时时间,超时后停止等待。

示例:

if (lock.tryLock()) {
    try {
        // 成功获取锁后执行
    } finally {
        lock.unlock();
    }
} else {
    // 没有成功获取锁
}

6. 条件变量(Condition

  • Synchronized:依赖于 Object 类的 wait()notify()/notifyAll() 方法实现线程的等待和通知机制,但每个锁只有一个等待队列。
  • ReentrantLock:支持多个条件变量,可以通过 newCondition() 方法创建多个 Condition 对象,以实现更加精细化的线程等待与通知机制。

示例:

Condition condition = lock.newCondition();
try {
    lock.lock();
    condition.await();  // 等待
    condition.signal();  // 唤醒
} finally {
    lock.unlock();
}

7. 性能与可扩展性

  • Synchronized:在 Java 1.6 及以上版本,经过多次优化(如自适应自旋锁、轻量级锁、偏向锁等),在大部分情况下性能表现较好,适合简单的同步场景。
  • ReentrantLock:性能稍高一些,尤其在高并发环境下,它提供了更多的控制能力,能够适应复杂的并发场景。

8. 可重入性

  • SynchronizedReentrantLock 都是可重入锁。也就是说,线程在持有锁的情况下可以多次获取该锁,而不会发生死锁。例如,同一个线程可以多次进入被 synchronized 修饰的代码块或调用多次 lock()

9. 锁的降级

  • Synchronized:不支持锁降级。
  • ReentrantLock:支持锁降级,即在持有写锁的情况下,可以降级为读锁(通过 ReadWriteLock 实现),以提升性能。

总结

特性SynchronizedReentrantLock
锁的获取与释放隐式显式
公平锁不支持支持公平与非公平两种模式
中断响应不支持支持 lockInterruptibly()
尝试获取锁不支持支持 tryLock()
多条件变量不支持支持多个 Condition
性能JVM 优化后性能较好提供更多功能和更灵活的控制
使用场景适用于简单的同步场景适用于复杂的并发控制场景

Synchronized 适合简单的同步需求,因为它的使用更为简洁且可以自动处理锁的释放。ReentrantLock 则提供了更强的控制能力,适合需要高级功能(如公平锁、中断、多个条件变量)的场景。

Java中的`synchronized`和`ReentrantLock`都是用来实现线程同步的机制,它们的作用是让多个线程按照一定的顺序执行,避免出现竞态条件(race condition)。 下面是`synchronized`和`ReentrantLock`的区别: 1. 语法:`synchronized`是Java语言内置的关键字,`ReentrantLock`是Java提供的一个类。 2. 锁的获取方式:`synchronized`是隐式获取锁,当线程进入同步块或同步方法时会自动获取锁并在执行完毕后释放锁;`ReentrantLock`则需要显式获取和释放锁,即在代码中通过调用`lock()`方法获取锁,在执行完毕后再通过调用`unlock()`方法释放锁。 3. 锁的可重入性:`synchronized`是可重入的,即同一个线程在已经获取到锁的情况下,可以重复获取该锁而不会发生死锁;`ReentrantLock`也是可重入的,但需要注意的是,每次获取锁都会增加锁的计数器,需要在释放锁时把计数器减一,否则将导致其他线程无法获取到锁。 4. 锁的可中断性:`synchronized`是不可中断的,即当一个线程获取到锁之后,其他线程只能等待锁被释放;`ReentrantLock`可以通过调用`lockInterruptibly()`方法实现可中断锁,即当其他线程在等待锁的时候,可以通过调用该方法中断当前线程的等待。 5. 条件变量:`ReentrantLock`提供了`Condition`接口来实现线程之间的协调与通信,而`synchronized`则没有。 总的来说,`ReentrantLock`比`synchronized`更加灵活和可控,但需要注意的是,使用`ReentrantLock`时需要手动管理锁的获取和释放,否则容易导致死锁;而`synchronized`则可以简化代码,但对于一些复杂的同步场景可能会有限制。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值