响应中断式的获取锁
在
Mutex
类中修改lockInterruptibly
方法如下:
@Overridepublic void lockInterruptibly() throws InterruptedException { sync.acquireInterruptibly(1); }
在
AQS
中加入如下代码:
private void doAcquireInterruptibly(int arg) throws InterruptedException { final Node node = addWaiter(Node.EXCLUSIVE); boolean failed = true; try { for (;;) { final Node p = node.predecessor(); if (p == head && tryAcquire(arg)) { setHead(node); p.next = null; // help GC failed = false; return; } /** * 当判断是被中断而不是被唤醒的时候,抛出InterruptedException * */ if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) // 2 throw new InterruptedException(); } } finally { if (failed) cancelAcquire(node); } } public final void acquireInterruptibly(int arg) throws InterruptedException { /** * 如果当前线程已经被中断了 直接抛出InterruptedException * 注意:Thread.interrupted()会在复位当前线程的中断状态 也就是变为false */ if (Thread.interrupted()) // 1 throw new InterruptedException(); // 尝试获取锁 如果获取不到则加入到阻塞队列中 if (!tryAcquire(arg)) doAcquireInterruptibly(arg); }
这里与
acquire(int arg)
有两点区别(分别在代码中的1和2处的代码):
1. 如果当前线程已经被中断了, 会抛出InterruptedException
,并且中断状态会被复位成false
,因为使用的是Thread.interrupted()
.
2. 在确定是被中断的时候,会抛出InterruptedException
,这里需要注意两点.注意:
1.parkAndCheckInterrupt()
中使用的是Thread.interrupted()
方法,因此该方法会把中断状态复位成false
,因此整个acquireInterruptibly(int arg)
方法如果抛出InterruptedException
异常的话中断状态也会被复位成false
.
2. 此时抛出异常,failed
依然为true
, 会执行cancelAcquire(node)
方法取消当前线程所对应的节点,也就是从等待队列中去除. 然后从doAcquireInterruptibly(int arg)
方法中退出.
从如下的流程图中可以更清楚的看看基本逻辑.
juc_5(3).png
接下来看个简单的例子测试一下
例子1 : 测试中断式获取锁
生成两个线程分别为
thread-1
和thread-2
, 让thread-1
获得锁,并让thread-2
加入该锁的等待队列中, 在thread-1
还没有释放锁前也就是thread-2
没有获得锁前中断thread-2
看看会发生什么.
import java.util.concurrent.TimeUnit;import com.sourcecode.locks.Test.Runner;public class TestLockInterruptedException { public static void main(String[] args) { Mutex m = new Mutex(); Thread thread_1 = new Thread(new Runner(m), "thread-1"); Thread thread_2 = new Thread(new Runner(m), "thread-2"); thread_1.start(); try { TimeUnit.SECONDS.sleep(1); //让thread-1获得锁 thread_2.start(); TimeUnit.SECONDS.sleep(1); //让thread-2充分进入到等待队列中 m.printWaitingNode(); } catch (InterruptedException e) { e.printStackTrace(); } thread_2.interrupt(); } static class Runner implements Runnable { Mutex m; public Runner(Mutex m) { this.m = m; } @Override public void run() { boolean getLock = true; try { m.lockInterruptibly(); } catch (Exception e) { e.printStackTrace(); //Thread.currentThread().interrupt(); //报告一下中断状态 因为抛出异常前中断状态被清空了 getLock = false; } System.out.println(Thread.currentThread().getName() + " runs, getLock: " + getLock); try { TimeUnit.SECONDS.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } if(getLock) m.unlock(); } } }
测试结果如下:
thread-2
会进入到catch
语句块中并且它的中断状态已经被复位了.
thread-1 runs, getLock: true[NULL,-1]->[thread-2,0]-> java.lang.InterruptedException thread-2 intrrupted status:falsethread-2 runs, getLock: false at com.sourcecode.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:357) at com.sourcecode.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:375) at com.sourcecode.locks.Mutex.lockInterruptibly(Mutex.java:41) at com.sourcecode.locks.TestLockInterruptedException$Runner.run(TestLockInterruptedException.java:32) at java.lang.Thread.run(Thread.java:745)
但是如果把
catch
语句块中的注释打开会发生什么呢?
thread-1 runs, getLock: true[NULL,-1]->[thread-2,0]-> java.lang.InterruptedException thread-2 intrrupted status:falsethread-2 runs, getLock: false at com.sourcecode.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:357) at com.sourcecode.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:375) at com.sourcecode.locks.Mutex.lockInterruptibly(Mutex.java:41) at com.sourcecode.locks.TestLockInterruptedException$Runner.run(TestLockInterruptedException.java:32) at java.lang.Thread.run(Thread.java:745) java.lang.InterruptedException: sleep interrupted at java.lang.Thread.sleep(Native Method) at java.lang.Thread.sleep(Thread.java:340) at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386) at com.sourcecode.locks.TestLockInterruptedException$Runner.run(TestLockInterruptedException.java:41) at java.lang.Thread.run(Thread.java:745)
可以从结果中看到
TimeUnit.SECONDS.sleep(10);
也抛出了异常,原因不难找到, 从下面sleep
的源码中可以看到如果当前线程的中断状态是true
的时候, 该方法会认为该线程被中断了,异常会抛出异常并且复位它的中断异常状态. 关于异常可以看我的另外一篇博客 [并发J.U.C] 用例子理解线程中断
* @throws InterruptedException * if any thread has interrupted the current thread. The * <i>interrupted status</i> of the current thread is * cleared when this exception is thrown. */ public static native void sleep(long millis) throws InterruptedException;
接下来看看
tryLock
方法.
tryLock方法 尝试性的去获取锁
那什么叫尝试性的去获取锁?在接口
Lock
中有定义
// 获取锁 如果锁是available立即返回true, 如果锁不存在就立即返回false boolean tryLock();
接下来看看是如何实现的, 先在
Mutex
类中修改
@Override public boolean tryLock() { // TODO Auto-generated method stub return sync.tryAcquire(1); }
可以看到很简单,直接调用了
sync
自己实现的tryAcquire
, 如果锁是可以得到的,则立即返回true
表明已经获得了锁, 否则立马返回, 不会进入到锁的等待队列中.
简单看一个
tryLock
的小例子
作者:nicktming
链接:https://www.jianshu.com/p/3a93aa207c56
共同学习,写下你的评论
评论加载中...
作者其他优质文章