JUC锁框架——初识AQS

AQS:AbstractQueuedSynchronizer

  • JUC:java/util/concurrent
  • Synchronized和ReentrantLock的实现原理是不一致的,Synchronized是依靠java虚拟机的功能实现的。ReentrantLock则是有AQS这样一个背后大Boss在提供帮助啊!

基础原理

JUC锁框架——初识AQS_第1张图片
JUC锁框架——初识AQS_第2张图片
JUC锁框架——初识AQS_第3张图片

Thread.sleep、Object.wait、LockSupport.park 区别

实际上AQS是用WAITING来模拟JVM的BLOCKED状态;用Condition的await()来模拟Object的wait()——这也是Java线程间通信(java-thread-signaling)的两种方式

而线程的blocked状态,是这样一种状态:
A thread that is blocked waiting for a monitor lock is in this state.

可见它似乎是monitor锁机制的一个专属状态,在ReentrantLock和Condition配套使用时,就不会有这个状态,我们知道Condition提供了await()和signal(),当调用了signal()是不是唤醒了await()呢?其实没有,此时的状态还是waiting状态,线程也没有真正被唤醒,唤醒是在锁unlock()的时候。那么signal()做了什么呢?它把条件队列中挂起的线程调入同步队列中了。

这,不就和synchronized实现机制类似么?在waiting状态的线程原本处于_WaitSet中,当notify之后,线程也没有被唤醒,而是被“转移”到_EntryList中,唤醒线程是在锁释放后。

Condition调用signal()后线程仍然是waiting状态,那synchronized为什么要另起一个blocked状态?因为通过优化,synchronized实现的锁比想象中复杂,《Java 并发实战》写到,JVM实现阻塞可以采用自旋等待,或者通过操作系统挂起阻塞线程,而synchronized最初尝试获取锁时会采用自旋的方式,同时,它还提供了AQS实现锁没有的一些策略选择,这些做法的目的是为了优化,但此时的状态可以说是blocked状态,并不是waiting状态。
JUC锁框架——初识AQS_第4张图片
JUC锁框架——初识AQS_第5张图片

代码讲解

https://www.jianshu.com/p/3112bb0364a0

独占式

    public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }
    // 把获取资源失败的node放入AQS等待队列
    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();//拿到node的前驱节点,赋值给p
                if (p == head && tryAcquire(arg)) {//如果p已经是头节点了,代表这个时候
//node是第二个节点,再次调用tryAcquire获取资源
                    setHead(node);//设置头节点
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                if (shouldParkAfterFailedAcquire(p, node) &&//判断此node是否可以被park
                    parkAndCheckInterrupt())//park
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

又是CAS自旋,首先拿到node的前驱节点,赋值给p,如果p已经是头节点了,代表这个时候node是第二个节点,再次尝试调用tryAcquire获取资源,如果成功,设置头节点为node,返回中断标记位,如果失败,先判断自己是否可以被park,如果可以的话,就park,等待unpark。

    //叫醒Next正常的节点
    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);
    }

https://www.cnblogs.com/zyrblog/p/9866140.html

JUC锁框架——初识AQS_第6张图片

你可能感兴趣的:(#,JUC)