ReentrantLock的公平锁和非公平锁图形流程及源码详解

在这里插入图片描述
公平锁流程及源码详解
在这里插入图片描述

如果这个方法返回true,那么获取锁成功;直接跳出执行这个线程的代码;

protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            //获取当前线程
            int c = getState();
            //获取当前锁状态
            if (c == 0) {//如果当前锁状态是0,也就是没有人持有锁那么尝试去获取锁
                if (!hasQueuedPredecessors() &&
                	//因为是公平锁所以要判断是否有线程的等待时间比这个线程长,如果没有就下一步,如果有那么返回false
                    compareAndSetState(0, acquires)) {
                    //将当前state状态改为1,就是当前锁已经有人获取了
                    setExclusiveOwnerThread(current);//设置占用排它锁的线程是当前线程
                    return true;//返回true那么执行线程其他代码
                }
            }
            else if (current == getExclusiveOwnerThread()) {//如果当前线程是本身就拥有锁,那么锁重入
                int nextc = c + acquires;//
                if (nextc < 0)//如果经过这一步那么是报错了,因为按道理
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);//状态加1
                return true; }//返回true获取锁成功
            return false; }

将线程加入队列中

private Node addWaiter(Node mode) {
        Node node = new Node(Thread.currentThread(), mode);//把当前线程作为node的一个属性,创建节点
        // Try the fast path of enq; backup to full enq on failure
        Node pred = tail;
        if (pred != null) {//判断尾部节点是否为空,第一次进来肯定是空的不执行
        //当尾部节点不为空后,节点就会在这边创建
            node.prev = pred;//使得当前节点的prev指向尾部节点
            if (compareAndSetTail(pred, node)) {//把尾部节点指向当前节点
                pred.next = node;//空元素pred的next指向node节点,可以理解成pred是当前拥有锁的线程,因为拥有锁了,所以离开队列留下一个空壳子
                return node;//结束
            }
        }
        enq(node);//第一次进来肯定会执行这个,创建队列,如果第一次同步进来多个线程,那么都会在这个方法里面创建队列和节点,之后的节点将会在上面创建
        return node;
    }

在tail节点为空的时候,如果有多个线程同时进入这个节点那么他会给这些线程都创建节点,如果没有大量线程同时进入那么就仅仅会初始化头部,尾部节点,创建一个空节点(理解成当前拥有锁的节点),和当前线程的节点。

 private Node enq(final Node node) {//
        for (;;) {
            Node t = tail;//第一次进来肯定是空的,这里可以理解成这个t是拥有锁的节点
            if (t == null) { // Must initialize
                if (compareAndSetHead(new Node()))//初始化头部节点
                    tail = head;//初始化尾部节点
            } else {
                node.prev = t;//在上面的方法有解释
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }
    }

获取锁失败那么要把当前线程加入队列并阻塞

final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;//默认线程没有被中断过,被中断过是需要返回中断过的状态给主流程,让我们自行做处理,比如lock.lockInterruptibly()方法就会抛出异常,要我们对其做处理
            for (;;) {
                final Node p = node.predecessor();//获取当前节点的前一个节点
                if (p == head && tryAcquire(arg)) {//判断他的前面的那个节点是否指向头部节点,如果指向了前面一个节点成立执行获取锁逻辑,上面已做解释
                    setHead(node);
                    //释放锁后,要把当前节点做为空节点指向头部节点
                    p.next = null; // help GC
                    //那么就要让当前线程的前一个节点报废,使得他的next指向空,没有了任何引用,那么自然会被gc回收
                    failed = false;
                    return interrupted;//这里返回中断信号回去,这里是false,表示该线程没有被中断过
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;//这里表示该线程被中断过,那么就需要我们对这个中断做处理,也可以选择不做处理我们的lock是不做处理的,就只是做了一个标记放在那边,我们程序员可以自身给他做处理
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }
 private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        int ws = pred.waitStatus;//获取上一个节点的状态,当前线程的是否需要被唤醒的状态是存在上一个节点的waitStatus中的
        if (ws == Node.SIGNAL)//如果上一个节点的状态是-1那么,就返回true,该节点可以在unlock()中被唤醒
            return true;
        if (ws > 0) {//果大于0那么就是出现异常被干掉了,正常情况下就走另一个选择
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            pred.next = node;
        } else {

            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);//设置上一个节点的状态为-1,就是可唤醒的状态
        }
        return false;
    }
private final boolean parkAndCheckInterrupt() {
        LockSupport.park(this);//阻塞线程
        return Thread.interrupted();//返回该线程是否被中断过,如果是正常唤醒那么会返回false,如果是通过中断的方式唤醒那么会返回true
    }

如果线程被中断过,设置中断标识,返回线程主流程

static void selfInterrupt() {
        Thread.currentThread().interrupt();
    }

非公平锁流程及源码详解
在这里插入图片描述

非公平锁与公平锁的不同在于在加锁过程中,非公平锁有两次获取锁的机会,不管队列中是否有线程在等待,直接暴力获取锁返回;当加入队列后其执行方式和公平锁就一样了,只能按照队列的顺序来拿锁;

 protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }

final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();//获取当前线程
            int c = getState();//获取当前锁状态
            if (c == 0) {
                if (compareAndSetState(0, acquires)) {//如果状态为0直接去修改线程状态
                    setExclusiveOwnerThread(current);//设置当前线程为独占线程
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值