可重入锁的实现

之前我们介绍了AQS的基本原理,从本节开始,我们就要分析各种实现了,本章就先分析可重入锁中的非公平锁的实现:

ReentrantLock lock = new ReentrantLock();
lock.lock();
lock.unlock();

这是我们的入口:

final void lock() {
			//通过CAS操作获得资源
            if (compareAndSetState(0, 1))
            	//成功后将当前线程设置为独占线程
                setExclusiveOwnerThread(Thread.currentThread());
            else
            	//按照正常流成获取资源
                acquire(1);
        }

线程首先会通过CAS操作尝试获取资源,如果成功了就将当前线程设置为独占线程,否则就按照正常流程来获取资源:

protected final void setExclusiveOwnerThread(Thread thread) {
        exclusiveOwnerThread = thread;
    }

成功直接设置独占线程。

public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

按照正常的流程就是我们之前分析的AQS的原理:

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

在这个方法里面调用了非公平锁自己实现的获取锁的方法:

final boolean nonfairTryAcquire(int acquires) {
			//获取当前请求资源的线程
            final Thread current = Thread.currentThread();
            //获得请求的资源的状态
            int c = getState();
            //如果状态是0,说明可以获取资源
            if (c == 0) {
            	//通过CAS操作获取指定数量的资源
                if (compareAndSetState(0, acquires)) {
                	//设置独占线程
                    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;
        }

首先回尝试通过独占的方式获取资源,如果失败,先判断当前资源的独占线程是否是当前线程,如果是,支持重入,可以继续获得资源,否则获取资源失败:

private Node addWaiter(Node mode) {
        Node node = new Node(Thread.currentThread(), mode);
        // Try the fast path of enq; backup to full enq on failure
        Node pred = tail;
        if (pred != null) {
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        enq(node);
        return node;
    }

正如之前分析的,将当前结点入队:

final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                final Node p = node.predecessor();
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

然后通过自旋的方式不断尝试获取资源,在中途判断前驱结点的状态,选在是否将自己挂起。当其他线程释放资源的时候做了什么:

protected final boolean tryRelease(int releases) {
			//计算出释放资源后的数目
            int c = getState() - releases;
            //判断当前线程是否是独占线程,如果不是报错
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            //如果释放资源后不为0,说明存在重入锁,否则,说明现在资源已经被全部释放,设置独占线程为null
            if (c == 0) {
                free = true;
                setExclusiveOwnerThread(null);
            }
            setState(c);
            return free;
        }

直接将释放的资源的数目修改到申请的资源中,并且判断是否存在可重入锁。

 private void unparkSuccessor(Node node) {
        /*
         * If status is negative (i.e., possibly needing signal) try
         * to clear in anticipation of signalling.  It is OK if this
         * fails or if status is changed by waiting thread.
         */
        int ws = node.waitStatus;
        if (ws < 0)
            compareAndSetWaitStatus(node, ws, 0);

        /*
         * Thread to unpark is held in successor, which is normally
         * just the next node.  But if cancelled or apparently null,
         * traverse backwards from tail to find the actual
         * non-cancelled successor.
         */
        Node s = node.next;
        if (s == null || s.waitStatus > 0) {
            s = 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);
    }

释放成功后会唤醒同步队列中头节点的继承者:

public static void unpark(Thread thread) {
        if (thread != null)
            UNSAFE.unpark(thread);
    }

继续进入自旋尝试去获得资源。我们发现非公平锁在获取资源的时候总是要先尝试通过CAS的方式直接获取资源,不成功的时候才会按照正常的流程去获取,我们接下来看一下非公平锁是如何获取资源的:

protected final boolean tryAcquire(int acquires) {
			//获取当前线程
            final Thread current = Thread.currentThread();
            //获取资源的状态
            int c = getState();
            //如果资源没有没其他线程获取
            if (c == 0) {
            	//如果同步队列里面没有处于等待的结点才会通过CAS成功获取到资源
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            //判断当前线程是否是独占线程,如果是可以继续获得资源
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

我们发现公平锁和非公平锁的唯一区别就是公平锁首先会判断同步队列里面有没有结点,没有再去尝试CAS获取,而非公平锁每次都是直接通过CAS尝试获取。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值