为了账号安全,请及时绑定邮箱和手机立即绑定

[Java源码][并发J.U.C]---用代码一步步实现AQS(2)---独占锁中断式获取

标签:
JAVA

响应中断式的获取锁

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)方法中退出.

从如下的流程图中可以更清楚的看看基本逻辑.


webp

juc_5(3).png

接下来看个简单的例子测试一下

例子1 : 测试中断式获取锁

生成两个线程分别为thread-1thread-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


点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消