美文网首页编程语言-Java系列
看!源码之ReentrantLock公平锁 下(内涵linux

看!源码之ReentrantLock公平锁 下(内涵linux

作者: starskye | 来源:发表于2020-03-14 21:51 被阅读0次

本章主要讲解lock的释放,如果未了解锁获取的读者可以查看上一篇。

    //属于ReentrantLock 内部实现使用的sync即FairSync类的release
    //但是FairSync并没有实现release最终实现的是他的父类Sync
    public void unlock() {
        sync.release(1);
    }
    //属于Sync arg参数为1
    public final boolean release(int arg) {
        //调用tryRelease 返回true 说明释放成功
        if (tryRelease(arg)) {
            //释放成功则需要唤醒下一个Node获取锁 获取当前head
            Node h = head;
            //如果h等于null 说明队列未创建,还并未初始化队列
            //并且当前waitStatus 不等于 0 ,因为等于零说明还未进行shouldParkAfterFailedAcquire
            //则没必要唤醒 因为在park前会多次检查队列,其中shouldParkAfterFailedAcquire
            //会绝对的保证head状态为-1 具体原因请查看 上篇文章
            if (h != null && h.waitStatus != 0)
                //如果存在唤醒Node则唤醒
                unparkSuccessor(h);
            return true;
        }
        return false;
    }
  //属于ReentrantLock 尝试释放当前锁
  protected final boolean tryRelease(int releases) {
            //当前锁的状态 默认一定是 1 而在每次重入锁的时候都会进行加1
            //代表着每次获取重入锁都需要有对应的释放锁 而此处则是释放锁的 -1操作
            int c = getState() - releases;
            //释放锁的线程不是锁持有的线程则抛出异常
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            //释放状态默认是false失败
            boolean free = false;
            //如果c等于0 说明所有重入所获取的锁都进行了释放已经没有持有锁的状态
            if (c == 0) {
                //则释放成功并且设置锁持有对象为null
                free = true;
                setExclusiveOwnerThread(null);
            }
            //设置当前锁状态 因为释放锁的操作必须是持有锁的线程进行的
            //所以此处仅仅是设置了state所以并不用cas操作因为其他线程都还卡着获取不了
            //只有当前state释放了并且唤醒next才会有新的线程获取锁
            //再次期间如果有新的线程进入则会加入到队列中而不是竞争锁
            setState(c);
            return free;
        }
      //属于AbstractQueuedSynchronizer
      //唤醒next Node
      private void unparkSuccessor(Node node) {
         //获取传入node的等待状态 切记 此处Node为head 并且一定会是head
        int ws = node.waitStatus;
        //如果当前head的ws小于0则重置 他为1
        if (ws < 0)
            compareAndSetWaitStatus(node, ws, 0);
        //获取node的next节点
        Node s = node.next;
        //如果当前节点为null 或者 ws状态大于0
        if (s == null || s.waitStatus > 0) {
            //不管是哪种都设置s为null 因为ws大于0 是取消状态需要跳过 null更不用说
            //这两种状态都不需要唤醒所以此处跳过他设置为null
            s = null;
            //从尾部开始遍历 t不等于null并且不等于当前对象 
            for (Node t = tail; t != null && t != node; t = t.prev)
                //直到获取到最后一个小于等于0的node设置为s
                //因为为了保证顺序性 一定是获取的是按顺序执行的一个
                //所以此处迭代不会停止会一直到结束 即t==node
                if (t.waitStatus <= 0)
                    s = t;
        }
        //s == null说明没有找到需要获取锁的Node 要么队列为空 要么都取消获取锁
        //相反s不等于null则唤醒s
        if (s != null)
            //这里需要注意的是 在获取锁的一篇中下方C代码第一段是每个Thread都有parker对象
            //而此对象存储这每个Thead的锁和唤醒条件所以此处唤醒需要传入需要唤醒的线程
            LockSupport.unpark(s.thread);
    }
    //属于LockSupport
    //唤醒指定线程
    public static void unpark(Thread thread) {
        //线程不等于null则调用UNSAFE的unpark方法
        if (thread != null)
            UNSAFE.unpark(thread);
    }

到此处又到了激动人心的jvm实现了。下面讲解unpark的C实现

//为了方便讲解流程将会跳过jvm的其他操作只管关键流程
//将传入的线程对象转换成 C++对象  unsafe.cpp 1300行
JavaThread thr = java_lang_Thread::thread(java_thread);
//调用thr的unpark方法 直接还有很多转换判断null之类的操作再次忽略
thr->parker()->unpark();

//其中的类在上篇已经介绍此处忽略忘记的小伙伴可以再去阅读一遍,回忆一下
//当前线程获取锁 锁的初始化在 线程创建时就初始化了而存储在Parker中
status = pthread_mutex_lock(_mutex);
//_cur_index在park时对_cond的索引做的记录所以此处可以直接唤醒
status = pthread_cond_signal (&_cond[_cur_index]);
//释放当前锁
status = pthread_mutex_unlock(_mutex);

Lock的公平锁到此基本结束,按照流程读者可以很快的了解他获取锁的流程,在简单的lock方法调用时整个系统发生的变化,从而学习设计者的设计思路。

相关文章

网友评论

    本文标题:看!源码之ReentrantLock公平锁 下(内涵linux

    本文链接:https://www.haomeiwen.com/subject/iwtoshtx.html