Java的Lock锁和synchronized关键字的区别及其使用

本文详细对比了Java中synchronized关键字与Lock接口的功能特性,包括它们的使用方式、优点及局限性,并介绍了Lock接口的实现类ReentrantLock以及ReentrantReadWriteLock,深入探讨了条件变量Condition的使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Java的Lock锁和synchronized关键字的区别及其使用

    一、synchronized关键字和Lock的区别以及优缺点

    我们可以回顾一下synchronized的使用,synchronized释放锁的时机有以下几种:
  1. 当执行完代码块中的代码,释放锁;
  2. 当代码抛出异常,释放锁;
  3. 当调用锁的wait方法,释放锁;

    以上三种情况便可以完全的帮我们解决线程同步的问题,为什么还要引入Lock呢?同样,synchronized关键字也有三种局限性:

  1. synchronized关键字无法响应中断,如果线程未获得锁,便会一直地尝试去获得锁,不会响应中断,lock锁的lockInterruptibly()方法能让线程响应中断,同时tryLock可以加入时间参数,若一定时间内未获得锁便返回,很灵活;
  2. synchronized关键字无法读写分离,在多个读线程访问临界资源时,是不需要同步的,但是synchronized关键字通通都给同步了,会导致效率很慢,Lock提供读写锁,实现读写分离;
  3. synchronized关键字无法知道线程是否获得锁;

    以上为synchronized的局限性,但是Lock也有他的缺点,Lock需要显式的释放锁,否则会造成死锁。

    接下来我们之上而下地看看Lock的类和接口吧...

    二、Lock接口

    Lock接口是最顶层的接口,提供的方法主要有这么几个:

    1、lock()

    lock()方法会尝试的获取锁,如果锁已经被其他线程给获取了,就会阻塞等待,需要注意的是,lock()需要显式地释放锁,如果发生异常也不会释放锁,所以很容易发生死锁,通常需要在finally中释放锁,保证锁一定被释放。

    2、tryLock()

    tryLock()会非阻塞地去获取锁,若获得锁则返回true,否则返回false,可以加入时间长度和时间单位参数,给线程一段等待的时间。

    3、lockInterruptibly()

    比较独特的一个用法,该方法获取锁时,若未获取到锁,等待阶段可以响应中断。在线程A和线程B竞争锁的时候,若线程B竞争失败,可以调用线程B的interrupt()方法打断线程B的阻塞状态。
    注意,interrupt()方法只能打断阻塞状态的线程,而不能打断已经获得锁的正在运行的线程。

    三、ReentrantLock类

    是唯一实现了Lock接口的类,重入锁(即,当一个线程已经获得了锁以后,当再次访问临界资源时,可以直接获得锁,synchronized关键字便是重入锁)。

    四、ReentrantReadWriteLock类

    ReentrantReadWriteLock类实现了ReadWriteLock接口,不过该接口只有两个方法,一个是获得读锁,一个是获得写锁。
    关于读写的冲突问题,掌握此三句:读读不冲突,读写冲突,写写冲突。

    五、Condition类

    Condition类提供wait()和notify()方法的替代,由锁实现,await()方法替代wait()方法,signal()和signalAll()方法替代notify()和notifyAll()方法,不同的是,一个Lock可以有多个Condition,而一个synchronized关键字只能有一个阻塞队列。

    如,在生产者消费者模型中,可以有两个Condition,一个用于提示仓库已满,一个用于提示仓库已空。

    以上便是Lock的全部内容,接下来介绍几种锁的概念:

    六、锁的概念

    1、重入锁

    上文已经提及,不赘述。

    2、可中断锁

    顾名思义,是可以响应中断的锁,synchronized关键字是非可中断锁,Lock是可中断锁。

    3、公平锁

    公平锁的含义就是,当多个线程等待同一个锁的时候,等待时间最久的线程获得即将释放的锁,即为公平,synchronized关键字是非公平的,Lock默认情况也是非公平的,但是可以在构造的时候,手动设置成公平锁。

    4、读写锁

    将读和写分为两个锁,使多个读线程不会相互等待。

    5、乐观锁和悲观锁

    两者都是顾名思义:乐观锁,就是一种很乐观的想法,认为自己在操作数据的时候,不会有其他任何的更新操作,所以在取数据的时候不加锁,只在提交更新的时候判断是否有人动过该数据,如使用版本号等机制。

    悲观锁,很悲观地认为在自己操作数据的时候,一定会有人操作数据,所以在取数据阶段就加锁,直到更新结束释放锁。数据库中的行锁,表锁,读锁,写锁都是悲观锁的实现。

    如此一分析,显然在读多写少的环境下,乐观锁会比较奏效,因为当并发量很大,乐观锁,便会不停地retry,降低效率。   

<think>嗯,用户问的是Java中的Locksynchronized能不能配合使用。首先,我需要确认这两者的基本概念。synchronizedJava关键字,用于实现同步,基于监视器(monitor lock),而Lock接口,比如ReentrantLock,是java.util.concurrent.locks包下的显式。它们的机制不同,但都用于线程同步。 接下来,得考虑它们是否可以一起使用。理论上,只要正确管理的获取释放,应该是可以的。但可能存在潜在问题,比如死或者顺序问题。比如,如果一个线程先获取synchronized,再尝试获取Lock,而另一个线程反过来,可能会造成死。这时候需要注意的顺序,或者是否有必要混合使用。 另外,还要考虑使用场景。混合使用可能会增加代码复杂度,降低可读性。但在某些情况下,比如遗留代码中已有synchronized,而新功能需要Lock的特性(如可中断、超时等),可能需要配合使用。这时候需要确保正确的资源管理异常处理,特别是Lock需要在finally块中释放。 然后,需要验证一些实际例子。比如,一个类的方法用了synchronized,另一个方法用了Lock,两者操作共享资源时,能否正确同步。可能由于不同的对象导致不同步。所以需要确认它们是否同一个对象或资源。如果synchronized定的是this,而Lock是另一个实例,那么可能无法正确同步,导致线程安全问题。 还要考虑可重入性。synchronizedReentrantLock都是可重入的,但混合使用时,线程可能持有多个不同的,需要确保不会导致意外的重入问题。 最后,总结是否推荐这样做。虽然技术上可行,但通常建议保持一致性,选择其中一种机制,以简化代码避免潜在问题。如果必须混合使用,必须严格管理的顺序范围,并进行充分的测试。</think>可以配合使用,但需要谨慎处理的获取释放顺序。以下是具体分析: --- ### 一、底层机制对比 1. **synchronized** - 基于JVM内置的监视器(Monitor) - 自动获取/释放(通过代码块进入/退出) - 不可中断等待(除非抛异常) 2. **Lock接口(如ReentrantLock)** - 基于代码实现的显式 - 需手动调用`lock()`/`unlock()` - 支持`tryLock()`、超时、可中断等特性 --- ### 二、配合使用的可行性 #### 1. 技术层面允许 ```java Object lock = new Object(); Lock reentrantLock = new ReentrantLock(); public void method() { synchronized(lock) { // 使用synchronized reentrantLock.lock(); // 再获取Lock try { // 操作共享资源 } finally { reentrantLock.unlock(); } } } ``` #### 2. 潜在风险 - **死风险**:若不同线程以相反顺序获取两种 ```java // 线程1顺序:synchronizedLock // 线程2顺序:Locksynchronized ``` - **粒度问题**:混合使用可能导致范围不可控 --- ### 三、典型使用场景 #### 1. 混合升级(需严格设计) - 先用`synchronized`快速处理简单同步 - 再通过`Lock`实现更精细控制(如超时回滚操作) #### 2. 第三方库兼容 - 当需要与使用`synchronized`的遗留代码交互时 --- ### 四、最佳实践建议 1. **避免嵌套混合** ```java // 不推荐写法 synchronized(obj) { lock.lock(); // 增加死风险 // ... } ``` 2. **统一策略** - 同一资源尽量选择单一同步机制 3. **顺序一致性** - 若必须混合,所有线程按相同顺序获取 --- ### 五、性能对比 | 特性 | synchronized | Lock | |---------------------|--------------|--------------| | 吞吐量(高竞争场景) | 较低 | 更高 | | 可中断性 | 不支持 | 支持 | | 超时机制 | 不支持 | 支持 | --- ### 总结 技术上可以配合使用,但推荐优先选择一种机制。若必须混合: 1. 通过`jstack`或可视化工具检测死 2. 使用`tryLock()`设置超时作为兜底 3. 在finally块确保释放所有
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值