在 Java 中,常用的锁有两种:synchronized(内置锁)和 ReentrantLock(可重入锁),二者的功效都是相同得,但又有很多不同点,所以我们今天就来聊聊。

区别1:用法不同

synchronized 可用来修饰普通方法、静态方法和代码块,而 ReentrantLock 只能用在代码块上。

synchronized 基础使用

使用 synchronized 修饰代码块:

public void method() {
// 加锁代码
synchronized (this) {
// ...
}
}

ReentrantLock 基础使用

ReentrantLock 在使用之前需要先创建 ReentrantLock 对象,然后使用 lock 方法进行加锁,使用完之后再调用 unlock 方法释放锁,具体使用如下:

public class LockExample {
// 创建锁对象
private final ReentrantLock lock = new ReentrantLock();
public void method() {
// 加锁操作
lock.lock();
try {
// ...
} finally {
// 释放锁
lock.unlock();
}
}
}

区别2:获取锁和释放锁方式不同

synchronized 会自动加锁和释放锁,当进入 synchronized 修饰的代码块之后会自动加锁,当离开 synchronized 的代码段之后会自动释放锁,如下图所示:



而 ReentrantLock 需要手动加锁和释放锁,如下图所示:

PS:在使用 ReentrantLock 时要特别小心,unlock 释放锁的操作一定要放在 finally 中,否者有可能会出现锁一直被占用,从而导致其他线程一直阻塞的问题。

区别3:锁类型不同

synchronized 属于非公平锁,而 ReentrantLock 既可以是公平锁也可以是非公平锁。

默认情况下 ReentrantLock 为非公平锁,这点查看源码可知:



使用 new ReentrantLock(true) 可以创建公平锁,查看源码可知:

区别4:响应中断不同

ReentrantLock 可以使用 lockInterruptibly 获取锁并响应中断指令,而 synchronized 不能响应中断,也就是如果发生了死锁,使用 synchronized 会一直等待下去,而使用 ReentrantLock 可以响应中断并释放锁,从而解决死锁的问题,比如以下 ReentrantLock 响应中断的示例:

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; public class ReentrantLockInterrupt {
static Lock lockA = new ReentrantLock();
static Lock lockB = new ReentrantLock();
public static void main(String[] args) throws InterruptedException {
// 线程 1:先获取 lockA 再获取 lockB
Thread t1 = new Thread(() -> {
try {
// 先获取 LockA
lockA.lockInterruptibly();
// 休眠 10 毫秒
TimeUnit.MILLISECONDS.sleep(100);
// 获取 LockB
lockB.lockInterruptibly();
} catch (InterruptedException e) {
System.out.println("响应中断指令");
} finally {
// 释放锁
lockA.unlock();
lockB.unlock();
System.out.println("线程 1 执行完成。");
}
});
// 线程 2:先获取 lockB 再获取 lockA
Thread t2 = new Thread(() -> {
try {
// 先获取 LockB
lockB.lockInterruptibly();
// 休眠 10 毫秒
TimeUnit.MILLISECONDS.sleep(100);
// 获取 LockA
lockA.lockInterruptibly();
} catch (InterruptedException e) {
System.out.println("响应中断指令");
} finally {
// 释放锁
lockB.unlock();
lockA.unlock();
System.out.println("线程 2 执行完成。");
}
});
t1.start();
t2.start();
TimeUnit.SECONDS.sleep(1);
// 线程1:执行中断
t1.interrupt();
}
}

以上程序的执行结果如下所示:

区别5:底层实现不同

synchronized 是 JVM 层面通过监视器(Monitor)实现的,而 ReentrantLock 是通过 AQS(AbstractQueuedSynchronizer)程序级别的 API 实现。

synchronized 通过监视器实现,可通过观察编译后的字节码得出结论,如下图所示:



其中 monitorenter 表示进入监视器,相当于加锁操作,而 monitorexit 表示退出监视器,相当于释放锁的操作。

ReentrantLock 是通过 AQS 实现,可通过观察 ReentrantLock 的源码得出结论,核心实现源码如下:

小结

synchronized 和 ReentrantLock 都是 Java 中提供的可重入锁,二者的主要区别有以下 5 个:

  1. 用法不同:synchronized 可以用来修饰普通方法、静态方法和代码块,而 ReentrantLock 只能用于代码块。
  2. 获取锁和释放锁的机制不同:synchronized 是自动加锁和释放锁的,而 ReentrantLock 需要手动加锁和释放锁。
  3. 锁类型不同:synchronized 是非公平锁,而 ReentrantLock 默认为非公平锁,也可以手动指定为公平锁。
  4. 响应中断不同:ReentrantLock 可以响应中断,解决死锁的问题,而 synchronized 不能响应中断。
  5. 底层实现不同:synchronized 是 JVM 层面通过监视器实现的,而 ReentrantLock 是基于 AQS 实现的。

是非审之于己,毁誉听之于人,得失安之于数。

公众号:Java面试真题解析

面试合集:https://gitee.com/mydb/interview

面试突击42:synchronized和ReentrantLock有什么区别?的更多相关文章

  1. 线程安全之 synchronized 和 ReentrantLock

    线程安全之 synchronized 和 ReentrantLock + 面试题 前面我们介绍了很多关于多线程的内容,在多线程中有一个很重要的课题需要我们攻克,那就是线程安全问题.线程安全问题指的是在 ...

  2. 面试官:你说说ReentrantLock和Synchronized区别

    大家好!又和大家见面了.为了避免面试尴尬,今天同比较通俗语言和大家聊下ReentrantLock和Synchronized区别! 使用方式 Synchronized可以修饰实例方法,静态方法,代码块. ...

  3. Synchronized 与 ReentrantLock 的区别!

    来源:cnblogs.com/baizhanshi/p/7211802.html 之前栈长分享了重入锁的概念:<到底什么是重入锁,拜托,一次搞清楚!>,今天现来深入了解下 Synchron ...

  4. 高级java必会系列二:多线程经常使用的3个关键字:synchronized、ReentrantLock、volatile

    系列一讲解了多线程,本章讲解多线程开发中经常使用到的3个关键字synchronized.ReentrantLock.volatile. 一.synchronized 互斥锁,即操作互斥,并发线程过来, ...

  5. synchronized和ReentrantLock

    一.什么是sychronized sychronized是java中最基本同步互斥的手段,可以修饰代码块,方法,类. 在修饰代码块的时候需要一个reference对象作为锁的对象. 在修饰方法的时候默 ...

  6. Synchronized与ReentrantLock区别总结(简单粗暴,一目了然)

    这篇文章是关于这两个同步锁的简单总结比较,关于底层源码实现原理没有过多涉及,后面会有关于这两个同步锁的底层原理篇幅去介绍. 相似点:这两种同步方式有很多相似之处,它们都是加锁方式同步,而且都是阻塞式的 ...

  7. synchronized和ReentrantLock的区别

    synchronized和ReentrantLock的区别 synchronized是和if.else.for.while一样的关键字,ReentrantLock是类,这是二者的本质区别. 代写 既然 ...

  8. java多线程系列6 synchronized 加强版 ReentrantLock

    ReentrantLock类是可重入.互斥.实现了Lock接口的锁,它与使用synchronized方法和快具有相同的基本行为和语义,并且扩展了其能力.ReenreantLock类的常用方法有: Re ...

  9. java的两种同步方式, Synchronized与ReentrantLock的区别

    java在编写多线程程序时,为了保证线程安全,需要对数据同步,经常用到两种同步方式就是Synchronized和重入锁ReentrantLock. 相似点: 这两种同步方式有很多相似之处,它们都是加锁 ...

随机推荐

  1. Java代码块与构造器方法执行顺序

    直接上源码: public class Demo4 { { //这里是代码块 System.out.println("这里是代码块"); } static { //这里是静态代码块 ...

  2. String--int互转

    A:int -->String 1.String s1 = "" + 100; 2.String s2 = String.valueof(100); 3.(int -- In ...

  3. 什么是root帐户?

    root帐户就像一个系统管理员帐户,允许你完全控制系统.你可以在此处创建和维护用户帐户,为每个帐户分配不同的权限.每次安装Linux时都是默认帐户.

  4. 什么是Hystrix断路器?我们需要它吗?

    由于某些原因,employee-consumer公开服务会引发异常.情况下使用Hystrix我们定义了回退方法.如果在公开服务中发生异常,则回退方法返回一些默认值 . 如果firstPage metho ...

  5. Mybatis框架基础入门(四)--SqlMapConfig.xml配置文件简介

    SqlMapConfig.xml中配置的内容和顺序如下: properties(属性) settings(全局配置参数) typeAliases(类型别名) typeHandlers(类型处理器) o ...

  6. 面试问题之计算机网络:TCP滑动窗口

    滑动窗口协议是传输层进行流量控制的一种措施,接收方通过通知发送方自己的窗口大小,从而控制发送方的发送速度,从而达到防止发送方发送速度过快而导致自己被淹没的目的,并且滑动窗口分为接收窗口和发送窗口.TC ...

  7. Java中带参数的方法和JavaScript中带参数的函数有什么不同?

    javascript是动态语言,是弱类型语言,其参数的使用很灵活:java则是强类型语言,参数的类型必须明确的

  8. Java 中的 TreeMap 是采用什么树实现的?

    Java 中的 TreeMap 是使用红黑树实现的.

  9. 智能指针中C++重载'->'符号是怎么实现的

    例如下面的代码: class StrPtr{ public: StrPtr() : _ptr(nullptr){} //拷贝构造函数等省略... std::string* operator->( ...

  10. 【译】客户端存储(Client-Side Storage)

    本文转载自:众成翻译译者:文蔺链接:http://www.zcfy.cc/article/660原文:http://www.html5rocks.com/en/tutorials/offline/st ...