【Java锁体系】五、隐式锁和显式锁的区别(Synchronized和Lock的区别)

本文聚焦Java并发编程中隐式锁和显式锁的区别。隐式锁以synchronized为代表,由JVM维护;显式锁以Lock为代表,是JDK5后出现的类。文章从出身、使用方式、等待是否中断、加锁公平性、锁绑定条件及性能等方面进行了详细对比。

目录:

【Java锁体系】一、悲观锁和乐观锁

【Java锁体系】二、Java中的CAS机制算法

【Java锁体系】三、自旋锁详解

【Java锁体系】四、Synchronized关键字详解

【Java锁体系】五、隐式锁和显氏锁的区别(Synchronized和Lock的区别)

【Java锁体系】六、AQS原理分析(AbstractQueuedSynchronizer实现分析)

【Java锁体系】七、JMM内存模型详解

【Java锁体系】八、MESI缓存一致性协议讲解

【Java锁体系】九、volatile关键字

【Java锁体系】五、隐式锁和显式锁的区别(Synchronized和Lock的区别)

在Java并发编程中,Java中隐式锁有synchronized,显式锁有Lock。

1.两者的出身不同

synchronized是Java中的关键字,由JVM维护,是JVM层面的锁;

而lock是JDK5之后才出现的具体的类,使用Lock是调用对应的API,是API层面的锁。

synchronized的底层是通过monitorenter进行加锁的(底层是通过monitor对象来完成的,其中的wait/notify等方法也是依赖monito对象的,只有在同步块或者是同步方法中才可以调用wait/notify方法。因为只有在同步块或同步方法中,JVM才会调用monitor对象的。);通过monitorexit来退出锁的。

Lock则是通过调用相应的API方法来获取和释放锁

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GSkVVUQw-1608212002139)(D:\software\typora\workplace\imgs_cas\27.jpg)]

2.使用方式的不同

synchronized隐式锁;lock是显式锁

显氏锁和隐式锁的区别在于:使用显式锁的时候,使用者需要手动去获取和释放锁。

在使用synchronized关键字的时候,使用者不需要写其他的代码,程序就可以自动获取锁和释放锁。synchronized是由系统维护的,系统会自动的让程序释放占用的锁。

在使用lock的时候,需要使用者手动去获取锁和释放锁。如果没有释放锁,就可能出现死锁的现象。手动获取锁的方法lock.lock(); 释放锁的操作:unlock().

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EhpupxQg-1608212002141)(D:\software\typora\workplace\imgs_cas\28.png)]

3.等待是否中断

synchronized关键字是不可中断的,除非抛出异常或者正常运行结束

而Lock是可以中断的,中断方式:

  • 调用设置超时方法tryLock(Long timeout,timeUnit unit)
  • 调用lockInterruptibly()放到代码块中,然后调用interrupt()方法可以中断。

4.加锁的时候是否公平

synchronized是非公平锁;

而lock两者都可以,默认创建是非公平锁。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dK4j6BIw-1608212002143)(D:\software\typora\workplace\imgs_cas\29.png)]

5.锁绑定多个条件来condition

synchronized要么随机唤醒一个线程;要么是唤醒所有等待的线程。

lock:可以用实现分组唤醒需要唤醒的线程,可以精准的唤醒,而不是像synchronized那样,不能精准唤醒线程。

6.性能比较

synchronized是托管给JVM去执行的,而Lock是Java写的控制锁的代码。在Java1.5中,synchronized的性能是低效的,因为其是重量级锁的操作,需要从用户态切换到内核态消耗大量的时间。相比之下使用Java提供的Lock对象,性能更高一些。但是到了Java1.6之后,对synchronized关键字进行了优化,有适应性自旋,锁消除,轻量级锁,偏向锁等,导致在Java1.6中synchronized的性能并不比lock差。

在之前的synchronized版本中使用是悲观锁的机制,即线程独占锁,其它线程只能依靠阻塞来等待线程释放的锁,而线程阻塞时会引起线程的上下文切换,当有很多线程竞争锁的时候,会引起CPU频繁的上下文切换导致效率很低。

而Lock使用的乐观锁的方式。所谓乐观锁就是,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。乐观锁的实现方式就CAS机制(compareAndSetstate),调用的是CPU提供的底层指令。

<think>我们被要求比较JavasynchronizedLock接口的差异。根据提供的引用,我们可以从多个维度进行对比:实现机制、功能特性、性能、使用方等。下面将系统性地整理这些差异。 ### 1. **实现机制** - **synchronized**: - 是Java语言内置的关键字,属于JVM层面的实现。 - 依赖于JVM的监视器(Monitor)机制,每个对象都与一个Monitor关联。 - 通过字节码指令`monitorenter`(获取`monitorexit`(释放)实现[^5]。 - **Lock**: - 是一个接口(`java.util.concurrent.locks.Lock`),属于API层面的。 - 需要地通过其实现类(如`ReentrantLock`)来创建对象。 - 的获取释放完全由代码控制[^3]。 ### 2. **功能特性** | **特性** | **synchronized** | **Lock** | |------------------|------------------------------------------|---------------------------------------------| | **可重入性** | 支持(同一线程可重复获取) | 支持(如`ReentrantLock`) | | **公平性** | 非公平(默认) | 可选择公平或非公平(通过构造参数) | | **中断响应** | 不支持等待中断 | 支持`lockInterruptibly()`方法实现中断响应 | | **超时机制** | 不支持 | 支持`tryLock(long time, TimeUnit unit)` | | **条件队列** | 通过`wait()`/`notify()`实现单一条件 | 支持多个条件(`Condition`对象) | | **绑定** | 与对象或类绑定 | 可独立于对象存在 | ### 3. **性能表现** - **历史演变**: - 早期JDK中,synchronized是重量级,性能较差(涉及用户态到内核态的切换)。 - JDK 1.6后,synchronized引入了**升级机制**(偏向→轻量级→重量级),性能大幅提升[^1][^4]。 - **Lock**: - 通常基于CAS(Compare-And-Swap)实现,减少上下文切换。 - 在高度竞争场景下,Lock的性能可能优于synchronized(如`ReentrantLock`的公平)[^1]。 ### 4. **使用方** - **synchronized**: - 使用简单,无需手动释放(JVM自动管理)。 - 三种应用方: 1. 修饰实例方法(当前对象): ```java public synchronized void method() { ... } ``` 2. 修饰静态方法(当前类的Class对象): ```java public static synchronized void method() { ... } ``` 3. 修饰代码块(指定对象): ```java synchronized (obj) { ... } ``` - **Lock**: - 需要(必须放在`try-finally`中确保解): ```java Lock lock = new ReentrantLock(); lock.lock(); try { // 临界区代码 } finally { lock.unlock(); // 必须手动释放 } ``` - 提供更灵活的方法: - `tryLock()`:尝试非阻塞获取。 - `lockInterruptibly()`:可中断地获取。 ### 5. **的释放** - **synchronized**: - 自动释放:当线程执行完同步代码块或方法时,JVM自动释放;若线程异常退出,也会自动释放。 - **Lock**: - 必须手动调用`unlock()`释放,否则会导致死。 - 通常将解操作放在`finally`块中确保执行[^3]。 ### 6. **适用场景** - **synchronized**: - 适合简单的同步场景(如单条件同步)。 - 代码简洁性要求高的场景(减少样板代码)[^1][^2]。 - **Lock**: - 需要高级功能的场景(如可中断、超时、公平)。 - 需要多个条件变量(`Condition`)的复杂同步逻辑[^1][^4]。 ### 总结对比表 | **维度** | **synchronized** | **Lock** | |------------------|------------------------------------------|---------------------------------------------| | **实现层面** | JVM内置 | API接口实现 | | **释放** | 自动释放 | 手动释放(必须写`unlock()`) | | **中断响应** | 不支持 | 支持 | | **公平** | 仅非公平 | 可配置公平/非公平 | | **条件队列** | 单一条件 | 支持多个条件(`newCondition()`) | | **代码复杂度** | 简单(语法糖) | 复杂(需`try-finally`块) | | **性能趋势** | JDK 1.6后优化,多数场景够用 | 高竞争场景可能更优 | > **建议**: > - 优先使用`synchronized`:满足需求时首选(简洁安全)。 > - 需要高级功能时选`Lock`:如超时控制、公平等[^1][^2]。 --- ### 相关问题 1. Java中如何实现的公平性与非公平性? 2. 什么是CAS操作?它在实现中起什么作用? 3. synchronized升级过程是怎样的? 4. 如何使用Condition实现多条件同步? 5. 读写(ReentrantReadWriteLock)与普通有何区别? [^1]: synchronizedLock的性能对比与应用场景 [^2]: synchronizedLock区别总结 [^3]: Lock接口的结构特性 [^4]: 读写的互斥规则 [^5]: synchronized的底层原理</think>### JavasynchronizedLock的深度对比 #### 一、**本质区别** | **维度** | **synchronized** | **Lock** | |------------------|------------------------------------------|---------------------------------------------| | **实现层级** | JVM内置关键字(语言级特性) | Java API接口(`java.util.concurrent.locks`) | | **释放机制** | 自动释放(代码块结束/JVM管理)[^3] | 手动释放(必须调用`unlock()`)[^3] | | **实现原理** | 基于对象监视器(Monitor)字节码指令`monitorenter/monitorexit`[^5] | 基于AQS(AbstractQueuedSynchronizer)队列实现 | #### 二、**功能特性对比** 1. **获取方** - `synchronized`:**不可中断**(线程阻塞时会一直等待) - `Lock`:支持`lockInterruptibly()`实现**可中断**(响应线程中断信号)[^1] 2. **公平性控制** - `synchronized`:**仅支持非公平** - `Lock`:可配置公平/非公平(通过`ReentrantLock(true)`实现公平)[^1] 3. **条件变量** - `synchronized`:依赖`wait()/notify()`实现**单一条件队列** - `Lock`:通过`newCondition()`创建**多个条件变量**(如生产者消费者独立等待队列)[^1] 4. **尝试机制** - `synchronized`:无超时机制 - `Lock`:支持`tryLock()``tryLock(long timeout, TimeUnit unit)`实现**限时等待**[^4] #### 三、**性能演变** | **JDK版本** | **synchronized优化** | **Lock特性** | |------------|------------------------------------------|-----------------------------------------| | ≤1.5 | 重量级(性能差) | 基于CAS的轻量级实现(性能优势明) | | ≥1.6 | 引入升级:无→偏向→轻量级→重量级[^1] | 保持稳定,高竞争场景仍具优势 | | ≥1.8 | 继续优化逃逸分析等机制 | 读写分离(`ReentrantReadWriteLock`)[^4] | > 💡 **性能建议**: > - 低竞争场景:优先用`synchronized`(JVM自动优化) > - 高竞争/复杂场景:用`Lock`(如需要公平或条件变量)[^1] #### 四、**代码实践对比** 1. **synchronized基础用法** ```java // 对象 public synchronized void syncMethod() { // 临界区代码 } // 类 public static synchronized void staticSyncMethod() { // 临界区代码 } // 代码块 public void blockSync() { synchronized(this) { // 对象可自定义 // 临界区代码 } } ``` 2. **Lock高级用法** ```java Lock lock = new ReentrantLock(); // 默认非公平 Condition condition = lock.newCondition(); public void lockExample() { lock.lock(); // 必须手动加 try { if (conditionNotMet) { condition.await(); // 释放并等待 } // 临界区代码 condition.signalAll(); // 唤醒等待线程 } finally { lock.unlock(); // 必须放在finally块 } } ``` #### 、**适用场景推荐** | **场景** | **推荐方案** | **原因** | |-----------------------------|----------------------|------------------------------------------| | 简单同步(如计数器递增) | `synchronized` | 代码简洁,JVM自动优化[^2] | | 需要公平性(如订单处理) | `ReentrantLock(true)`| 避免线程饥饿问题[^1] | | 读写分离(如缓存系统) | `ReentrantReadWriteLock` | 读操作完全并行,写操作互斥[^4] | | 复杂条件等待(如线程池调度) | `Lock`+`Condition` | 多条件队列控制更灵活[^1] | > ⚠️ **风险提示**: > 使用`Lock`时必须用`try-finally`保证解,否则会导致**死**(忘记`unlock()`的概率远高于`synchronized`自动释放)[^3]。 #### 六、**设计哲学差异** - **synchronized**:体现Java的**哲学**(“即对象”),简化开发但灵活性受限。 - **Lock**:体现**显式锁哲学**(“是独立资源”),通过API暴露控制细节,适合复杂场景[^3]。 --- ### 相关问题 1. 如何通过`ReentrantReadWriteLock`实现高性能缓存? 2. synchronized升级过程具体如何运作? 3. 什么是AQS(AbstractQueuedSynchronizer)?它在Lock中起什么作用? 4. 如何诊断解决Java中的死问题? 5. 无编程(如CAS)与Lock/synchronized有何本质区别? [^1]: synchronizedLock的性能对比与应用场景 [^2]: synchronizedLock的基础区别 [^3]: Lock接口的结构特性与设计哲学 [^4]: 读写的互斥规则 [^5]: synchronized的底层实现原理
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

mind_programmonkey

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

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

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

打赏作者

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

抵扣说明:

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

余额充值