(2)支持超时 。如果线程在一段时间之内没有获取到锁,不是进入阻塞状态,而是返回一个错误,那这个线程也有机会释放曾经持有的锁 。这样也能破坏不可剥夺条件 。
(3)非阻塞地获取锁 。如果尝试获取锁失败,并不进入阻塞状态,而是直接返回,那这个线程也有机会释放曾经持有的锁 。这样也能破坏不可剥夺条件 。
体现在Lock接口上,就是Lock接口提供的三个方法,如下所示 。
// 支持中断的APIvoid lockInterruptibly() throws InterruptedException;// 支持超时的APIboolean tryLock(long time, TimeUnit unit) throws InterruptedException;// 支持非阻塞获取锁的APIboolean tryLock();
- lockInterruptibly()
- tryLock()方法
- tryLock(long time, TimeUnit unit)方法
也就是说,对于死锁问题,Lock能够破坏不可剥夺的条件,例如,我们下面的程序代码就破坏了死锁的不可剥夺的条件 。
public class TansferAccount{ private Lock thisLock = new ReentrantLock(); private Lock targetLock = new ReentrantLock(); //账户的余额 private Integer balance; //转账操作 public void transfer(TansferAccount target, Integer transferMoney){ boolean isThisLock = thisLock.tryLock(); if(isThisLock){ try{ boolean isTargetLock = targetLock.tryLock(); if(isTargetLock){ try{ if(this.balance >= transferMoney){ this.balance -= transferMoney; target.balance += transferMoney; } }finally{ targetLock.unlock } } }finally{ thisLock.unlock(); } } }}例外,Lock下面有一个ReentrantLock,而ReentrantLock支持公平锁和非公平锁 。
在使用ReentrantLock的时候,ReentrantLock中有两个构造函数,一个是无参构造函数,一个是传入fair参数的构造函数 。fair参数代表的是锁的公平策略,如果传入true就表示需要构造一个公平锁,反之则表示要构造一个非公平锁 。如下代码片段所示 。
//无参构造函数: 默认非公平锁public ReentrantLock() {sync = new NonfairSync();}//根据公平策略参数创建锁public ReentrantLock(boolean fair){sync = fair ? new FairSync() : new NonfairSync();}锁的实现在本质上都对应着一个入口等待队列,如果一个线程没有获得锁,就会进入等待队列,当有线程释放锁的时候,就需要从等待队列中唤醒一个等待的线程 。如果是公平锁,唤醒的策略就是谁等待的时间长,就唤醒谁,很公平; 如果是非公平锁,则不提供这个公平保证,有可能等待时间短的线程反而先被唤醒 。而Lock是支持公平锁的,synchronized不支持公平锁 。
最后,值得注意的是,在使用Lock加锁时,一定要在finally{}代码块中释放锁,例如,下面的代码片段所示 。
try{ lock.lock();}finally{ lock.unlock();}注:其他synchronized和Lock的详细说明,小伙伴们自行查阅即可 。
点击关注,第一时间了解华为云新鲜技术~
经验总结扩展阅读
- 附:2种实现方式详细对比 Java 动态代理原理图解
- 深度剖析Java的volatile实现原理,再也不怕面试官问了
- 6 Java多线程:锁与AQS(下)
- 三十九 Java开发学习----SpringBoot整合mybatis
- JavaSPI详解
- 2023年1月搬家最佳吉日及入门时间
- day04-JavaScript01
- Java安全之Tomcat6 Filter内存马
- 前女友有了新男朋友还能挽回吗
- 帅农鸟哥是什么梗