当前位置: 首页 > 科技观察

什么是可中断锁?有什么用?如何实现?

时间:2023-03-23 12:08:31 科技观察

作者|王磊来源|Java中文社区(ID:javacn666)授权请联系(微信ID:GG_Stone)Java中的锁有两种,一种是内置锁synchronized,一种是显示锁Lock,其中Lock锁是可中断的lock,而synchronized是不可中断的锁,所谓中断锁是指锁在执行过程中可以被中断,也就是在执行过程中可以收到中断通知,从而打断锁的执行。PS:默认Lock同样是不间断锁,但是可以通过特殊的“手段”变成可中断锁。下面我们一起来看一下,为什么需要可中断锁呢?不间断锁的问题在于,当“异常”发生时,可以只能阻塞等待,没有别的办法,比如下面这个程序。下面程序中有两个线程,其中线程1首先获取锁资源执行相应的代码,线程2在0.5s后开始尝试获取锁资源,但是线程1在执行时忘记释放锁,从而导致线程2阻塞等待的情况,实现代码如下:=newReentrantLock();//创建线程1Threadt1=newThread(newRunnable(){@Overridepublicvoidrun(){lock.lock();System.out.println("线程1:获取锁。");//线程1没有释放锁}});t1.start();//创建线程2Threadt2=newThread(newRunnable(){@Overridepublicvoidrun(){//先休眠0.5s,让线程1执行try{Thread.sleep(500);}catch(InterruptedExceptione){e.printStackTrace();}//获取锁System.out.println("Thread2:Waitingtogetthelock.");lock.lock();try{System.out.println("线程2:成功获取锁。");}finally{lock.unlock();}}});t2.start();}}上面代码执行结果如下:从上面的结果可以看出线程2正在等待操作ofacquirethelock,buthasexperiencedAfterlongtime...再次查看结果,还是熟悉的画面:线程2依然阻塞等待获取线程1释放锁资源。这个时候,线程2除了等待,别无他法。而且,当我们熟练地取出JConsole,尝试获取死锁的具体信息时,得到的结果是:没有检测到死锁信息。从上图我们可以看出,当只有一个锁资源时,系统不会将这种情况判断为死锁,当然也没有关于阻塞和等待的具体信息。这时,只有线程2独自等待它的“锁”。使用中断锁然而,中断锁的出现可以打破这种僵局。可以在等待一定时间后主动中断线程2,解决线程阻塞等待的问题。中断锁的核心实现代码是lock.lockInterruptibly()方法,与lock.lock()方法类似,只是可以先使用lockInterruptibly方法接收中断请求。中断锁的具体实现如下:importjava.util.concurrent。locks.Lock;importjava.util.concurrent.locks.ReentrantLock;publicclassInterruptiblyExample{publicstaticvoidmain(String[]args)throwsInterruptedException{Locklock=newReentrantLock();//创建线程1Threadt1=newThread(newRunnable(){@Overridepublicvoidrun(){try{//加锁操作lock.lock();System.out.println("Thread1:Getthelock.");}catch(InterruptedExceptione){e.printStackTrace();}//线程1没有释放锁}});t1.start();//创建线程2Threadt2=newThread(newRunnable(){@Overridepublicvoidrun(){//先休眠0.5s,让线程1执行try{Thread.sleep(500);}catch(InterruptedExceptione){e.printStackTrace();}//获取锁try{System.out.println("线程2:尝试获取锁。");lock.lockInterruptibly();//可中断锁System.out.println("线程2:成功获取锁。");}catch(InterruptedExceptione){System.out.println("线程2:执行已被中断。");}}});t2.start();//之后等待2s,终止线程2Thread.sleep(2000);if(t2.isAlive()){//线程2还在执行System.out.println("执行线程中断。");t2.interrupt();}else{System.out.println("线程2:执行完成。");}}}以上代码执行结果如下:从上面的结果可以看出,我们在使用lockInterruptibly方法的时候,可以判断一段时间后是否还在阻塞等待。如果结果为真,我们可以直接中断,如上图但是当我们尝试将lockInterruptibly方法替换成lock方法时(其他代码不变),执行结果就完全不同了。实现代码如下:importjava.util.concurrent.locks.Lock;importjava.util.concurrent.locks.ReentrantLock;publicclassInterruptiblyExample{publicstaticvoidmain(String[]args)throwsInterruptedException{Locklock=newReentrantLock();//创建线程1Threadt1=newThread(newRunnable(){@Overridepublicvoidrun(){try{//锁操作lock.lockInterruptibly();System.out.println("线程1:获取锁。");}catch(InterruptedExceptione){e.printStackTrace();}//线程1没有释放锁}});t1.start();//创建线程2Threadt2=newThread(newRunnable(){@Overridepublicvoidrun(){//先休眠0.5s,让线程1执行try{Thread.sleep(500);}catch(InterruptedExceptione){e.printStackTrace();}//得到一个locktry{System.out.println("线程2:尝试获取锁。");lock.lock();System.out.println("线程2:获取锁成功。");}catch(Exceptione){System.out.println("线程2:执行已中断。");}}});t2.start();//等待2s后,终止线程2Thread.sleep(2000);if(t2.isAlive()){//线程2还在执行System.out.println("Executionthreadinterrupt.");t2.interrupt();}else{系统.out.println("Thread2:Executioncompleted.");}}}上面程序的执行结果如下:从上图可以看出,当使用lock方法时,即使中断方法是调用,线程2仍然不能被中断。本文介绍中断锁的实现,通过展示锁的lockInterruptibly方法来完成。和lock方法类似,但是lockInterruptibly可以先收到中断通知,而lock方法只能“死等”锁资源的释放。这两种方法的区别也是常见的面试题,希望这篇文章对你有用。