码迷,mamicode.com
首页 > 其他好文 > 详细

第六章 ReentrantLock源码解析2--释放锁unlock()

时间:2016-01-15 20:27:08      阅读:192      评论:0      收藏:0      [点我收藏+]

标签:

最常用的方式:

        int a = 12;
        //注意:通常情况下,这个会设置成一个类变量,比如说Segement中的段锁与copyOnWriteArrayList中的全局锁
        final ReentrantLock lock = new ReentrantLock();
        
        lock.lock();//获取锁
        try {
            a++;//业务逻辑
        } catch (Exception e) {
        }finally{
            lock.unlock();//释放锁
        }

注:关于lock()方法的源码解析,请参照"第五章 ReentrantLock源码解析1--获得非公平锁与公平锁lock()",具体链接如下:

http://www.cnblogs.com/java-zhao/p/5131544.html

 

释放锁:unlock()

步骤:

1)获取当前的锁数量,然后用这个锁数量减去解锁的数量(这里为1),最后得出结果c

2)判断当前线程是不是独占锁的线程,如果不是,抛出异常

3)如果c==0,说明锁被成功释放,将当前的独占线程置为null,锁数量置为0,返回true

4)如果c!=0,说明释放锁失败,锁数量置为c,返回false

5)如果锁被释放成功的话,唤醒距离头节点最近的一个非取消的节点

源代码:

 ReentrantLock:unlock()

技术分享
    /**
     * 释放这个锁
     *1)如果当前线程持有这个锁,则锁数量被递减
     *2)如果递减之后锁数量为0,则锁被释放。
     *如果当前线程不持久有这个锁,抛出异常
     */
    public void unlock() {
        sync.release(1);
    }
View Code

AbstractQueuedSynchronizer:release(int arg)

技术分享
    /**
     * 释放锁(在独占模式下)
     */
    public final boolean release(int arg) {
        if (tryRelease(arg)) {//如果成功释放锁
            Node h = head;//获取头节点:(注意:这里的头节点就是当前正在释放锁的节点)
            if (h != null && h.waitStatus != 0)//头结点存在且等待状态不是取消
                unparkSuccessor(h);//唤醒距离头节点最近的一个非取消的节点
            return true;
        }
        return false;
    }
View Code

Sync:tryRelease(int releases)

技术分享
        /**
         * 释放锁
         */
        protected final boolean tryRelease(int releases) {
            int c = getState() - releases;//获取现在的锁数量-传入的解锁数量(这里为1)
            if (Thread.currentThread() != getExclusiveOwnerThread())//当前线程不持有锁
                throw new IllegalMonitorStateException();
            boolean free = false;
            if (c == 0) {//锁被释放
                free = true;
                setExclusiveOwnerThread(null);
            }//如果不为0,怎么办,不释放了吗?
            setState(c);
            return free;
        }
View Code

AbstractQueuedSynchronizer:unparkSuccessor(Node node)

技术分享
    /**
     * 唤醒离头节点node最近的一个非取消的节点
     * @param node 头节点
     */
    private void unparkSuccessor(Node node) {
        /*
         * 将当前节点node的状态(如果<0的话)改为0,这个不知道有什么必要,
         * node节点就是头节点,在下边的代码中头节点都要被替换了,也就是说node节点就要被踢出队列了,
         * 这个时候还有什么必要去改他的状态吗?
         */
        int ws = node.waitStatus;
        if (ws < 0)
            compareAndSetWaitStatus(node, ws, 0);

        /*
         * 获取头节点的下一个等待状态不是cancel的节点
         */
        Node s = node.next;//头节点的下一个节点
        if (s == null || s.waitStatus > 0) {
            s = null;
            /*
             * 注意:从后往前遍历找到离头节点最近的一个非取消的节点,从后往前遍历据说是在入队(enq())的时候,可能nodeX.next==null,但是在读源码的时候没看出来
             */
            for (Node t = tail; t != null && t != node; t = t.prev)
                if (t.waitStatus <= 0)
                    s = t;
        }
        if (s != null)
            LockSupport.unpark(s.thread);//唤醒离头节点最近的一个非取消的节点
    }
View Code

注意:

在程序的注释部分有一些疑问,整理成下边这样:

  • 如果按照开头的那个程序的话,成功的获取一个锁之后,就会在finally里边解一次锁,可重入性怎么体现?
  • 在unparkSuccessor(Node node)这个方法中,一开始的时候将当前节点node的状态(如果<0的话)改为0,这个不知道有什么必要?(node节点就是头节点,在下边的代码中头节点都要被替换了,也就是说node节点就要被踢出队列了,这个时候还有什么必要去改他的状态吗?)
  • 在找到离头节点最近的一个非取消的节点,是以从后往前的方式进行的,原因是"从后往前遍历据说是在入队(enq())的时候,可能nodeX.next==null",但是在读源码的时候没看出来

 第一个问题答案:

可重入性体现在下边这个程序(就是锁套锁,最常见的就是在递归中):

技术分享
    final ReentrantLock lock = new ReentrantLock();
    public void add(){
        lock.lock();//获取锁
        try {
            add();//业务逻辑
        } catch (Exception e) {
        }finally{
            lock.unlock();//释放锁
        }
    }
View Code

注意:

  • 上边这个程序只是一个示例,在递归的使用中,一定要有递归结束的条件
  • 每有一个lock()方法,就有一个unlock()与之对应,所以在解锁的时候,只需要把传递解锁数量为1就可以。

第二个问题答案:

还在研究(望大神指导)

第三个问题答案

还在研究(望大神指导)

 

第六章 ReentrantLock源码解析2--释放锁unlock()

标签:

原文地址:http://www.cnblogs.com/java-zhao/p/5133402.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!