ReentrantLock原码学习:二、公平锁

ReentrantLock原码学习:公平锁

书接上文,公平锁和非公平锁的不同,主要体现在锁的竞争方式不同。

公平锁在获取锁的时候,同样分三种情况:第一种:在查到当前锁状态为0的时候(即无线程持锁),并不会立即去竞争锁,而是先通过 hasQueuedPredecessors()方法判断CLH锁队列中是否还有等待已久的线程,如果有,那么当前线程乖乖去锁队列的尾部去排队,讲究个先来后到;如果队列中没有等待线程,那么当前线程就去竞争锁,成功后,将当前线程设置为锁的持有者;如果锁状态不为0,判断是不是自己持有了锁,如果是,则修改锁状态加1,这就是锁重入;第三种,锁被占有,但不是自己持有,那么乖乖去CLH锁队列尾部排队。
        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
            //判断当前的CLH锁队列中是否还有排在前面的线程
                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;
        }

锁的释放

无论是公平锁还是非公平锁,释放锁的方式都是统一的,通过修改AQS中锁状态state的值(每次释放减1)来释放,当state为0时,将AQS中ExclusiveOwnerThread置为null,表示当前线程释放掉锁。

rotected final boolean tryRelease(int releases) {
            int c = getState() - releases;
            //判断释放锁的线程是否是持锁线程
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            //锁状态为0,则将ExclusiveOwnerThread置空,锁被释放
            if (c == 0) {
                free = true;
                setExclusiveOwnerThread(null);
            }
            setState(c); //修改锁状态,
            return free;
        }

释放锁成功后,需要唤醒CLH锁队列中的后继节点。这里由父类AQS中实现:

public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            //如果HEAD节点存在,等待状态不是初始化状态,就唤醒HEAD的后继节点
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }

公平锁与非公平锁总结

  • 无论是公平锁还是非公平锁,都是可重入锁,判断持锁线程是自己后,修改锁状态state,且在第一次获取锁时,ExclusiveOwnerThread置为当前线程;
  • 释放锁的时候,也是修改锁状态state的值,为0的时候,ExclusiveOwnerThread置空,表示锁释放,同时会唤醒AQS的锁等待队列中,HEAD节点的后继节点(waitStatus<=0,未取消的节点);
  • 公平锁在竞争锁的时候,会先判断CLH锁队列中是否有等待已久的线程,有,则放弃本次竞争,作为尾节点插入CLH队列等待唤醒,讲究先来后到;
  • 非公平锁在竞争锁的原则是:只要当前锁未被持有,就去竞争,不用管队列里是否有线程等待;

你可能感兴趣的:(java)