AQS-基于JDK17

概括

全称是 AbstractQueuedSynchronizer,AbstractQueuedSynchronizer抽象类(以下简称AQS)是整个java.util.concurrent包的核心。是阻塞式锁和相关的同步器工.在JDK1.5时,Doug Lea引入了J.U.C包,该包中的大多数同步器都是基于AQS来构建的。AQS框架提供了一套通用的机制来管理同步状态(synchronization state)、阻塞/唤醒线程、管理等待队列。

我们所熟知的ReentrantLock、CountDownLatch、CyclicBarrier等同步器,其实都是通过内部类实现了AQS框架暴露的API,以此实现各类同步器功能。这些同步器的主要区别其实就是对同步状态(synchronization state)的定义不同。

同步器资源的定义
ReentrantLock资源表示独占锁。State为0表示锁可用;为1表示被占用;为N表示重入的次数
CountDownLatch资源表示倒数计数器。State为0表示计数器归零,所有线程都可以访问资源;为N表示计数器未归零,所有线程都需要阻塞。
Semaphore资源表示信号量或者令牌。State≤0表示没有令牌可用,所有线程都需要阻塞;大于0表示由令牌可用,线程每获取一个令牌,State减1,线程没释放一个令牌,State加1。
ReentrantReadWriteLock资源表示共享的读锁和独占的写锁。state逻辑上被分成两个16位的unsigned short,分别记录读锁被多少线程使用和写锁被重入的次数

特点

  • 用 state 属性来表示资源的状态(分独占模式和共享模式),子类需要定义如何维护这个状态,控制如何获取锁和释放锁

    • getState - 获取 state 状态 1 表示获取占用锁,0 表示释放锁
    • setState - 设置 state 状态
    • compareAndSetState - cas 机制设置 state 状态,保证state原子性
    • 独占模式是只有一个线程能够访问资源,而共享模式可以允许多个线程访问资源
  • 提供了基于 FIFO 的等待队列,类似于 Monitor 的 EntryList

  • 条件变量来实现等待、唤醒机制,支持多个条件变量,类似于 Monitor 的 WaitSet

子类主要实现这样一些方法(默认抛出 UnsupportedOperationException)

钩子方法描述
tryAcquire排它获取(资源数)
tryRelease排它释放(资源数)
tryAcquireShared共享获取(资源数)
tryReleaseShared共享获取(资源数)
isHeldExclusively是否排它状态

AQS 使用的 CLH 锁,需要一个虚拟 head 节点,这个节点的作用是防止重复释放锁。当第一个进入队列的节点没有前置节点的时候,就会创建一个虚拟的

AQS JDK14 以后大改,获取锁流程整合到aquice方法中,移除waitStatus 属性,并简化锁的状态标志位(减少为3个),最新版本JDK17 则没有改动

相比以前实现更简单效率更高

重要属性

/**
 *  等待队列的头节点。懒加载初始化
 */
private transient volatile Node head;

/**
 * 等待队列的尾节点. 初始化后使用 casTail 修改.
 */
private transient volatile Node tail;

/**
 *  锁的状态,1 持有锁,0 未持有锁
 */
private volatile int state;

线程在等待队列中的状态:

    static final int WAITING   = 1;          // 线程等待
    static final int CANCELLED = 0x80000000; // 常量负数,表示取消等待
    static final int COND      = 2;          // 使用条件变量

AQS内部节点

在这里插入图片描述

abstract static class Node {
        volatile Node prev;       // initially attached via casTail
        volatile Node next;       // visibly nonnull when signallable
        Thread waiter;            // visibly nonnull when enqueued
        volatile int status;      // written by owner, atomic bit ops by others

        // methods for atomic operations
        final boolean casPrev(Node c, Node v) {  // for cleanQueue
            return U.weakCompareAndSetReference(this, PREV, c, v);
        }
        final boolean casNext(Node c, Node v) {  // for cleanQueue
            return U.weakCompareAndSetReference(this, NEXT, c, v);
        }
        final int getAndUnsetStatus(int v) {     // for signalling
            return U.getAndBitwiseAndInt(this, STATUS, ~v);
        }
        final void setPrevRelaxed(Node p) {      // for off-queue assignment
            U.putReference(this, PREV, p);
        }
        final void setStatusRelaxed(int s) {     // for off-queue assignment
            U.putInt(this, STATUS, s);
        }
        final void clearStatus() {               // for reducing unneeded signals
            U.putIntOpaque(this, STATUS, 0);
        }

        //获取节点属性的内存中的偏移量
        private static final long STATUS
            = U.objectFieldOffset(Node.class, "status");
        private static final long NEXT
            = U.objectFieldOffset(Node.class, "next");
        private static final long PREV
            = U.objectFieldOffset(Node.class, "prev");
    }

独占节点

static final class ExclusiveNode extends Node { }

共享节点

static final class SharedNode extends Node { }

条件变量节点

static final class ConditionNode extends Node
        implements ForkJoinPool.ManagedBlocker {
        ConditionNode nextWaiter;            // link to next waiting node

        /**
        允许在ForkJoinPools中使用条件变量,而不会有固定池耗尽的风险。这仅用于未计时条件等待,而非计时版本。
         */
        public final boolean isReleasable() {
            return status <= 1 || Thread.currentThread().isInterrupted();
        }

        public final boolean block() {
            while (!isReleasable()) LockSupport.park();
            return true;
        }
    }

方法解析

1、看锁标志位,默认是θ,所以这时候有线程抢到锁 
需要一种机制 
CAS机制 
2 有线程占有了锁,这时候有线程来抢锁 
   1、当前来抢锁的线程是不是占有锁的线程 
        是 重入 state+1 ,释放也需要释放对应次数
   2、如果不是抢锁失败 
     
3、优化:hasQueuedThreads()看等待队列是否有其他线程正在等待获取锁,如果有,则对于公平锁则不会进行获取

公平锁: 按照请求锁的顺序分配 拥有稳定获得锁的机会 但是性能可能比非公平锁低 只要按请求顺序就一定能获得锁

非公平锁 :不按照请求锁的顺序分配 不一定拥有获得锁的机会 。但是性能可能比公平锁高 ,非公平会意味着后请求锁的线程可能在其他线程休眠之前获得锁 提高效率,因为唤醒挂起的线程

非公平锁可以利用线程切换的延时来完成获取锁的操作
公平锁、非公平锁存在差异的 
唯一的差别就是占有锁的执行完临界区的代码线程释放了锁,此时非公平锁时恰好抢到了锁,而公平锁则需要从等待区域进去等待

尝试获取锁:此方法由其子类实现

protected boolean tryAcquire(int arg) {
        throw new UnsupportedOperationException();
 }

获取锁

 public final void acquire(int arg) {
        if (!tryAcquire(arg))
            acquire(null, arg, false, false, false, 0L);
    }

核心方法 acquire

final int acquire(Node node, int arg, boolean shared,
                      boolean interruptible, boolean timed, long time) {
        Thread current = Thread.currentThread();
        byte spins = 0, postSpins = 0;   
    //两变量作用: 如果一个线程进入阻塞逻辑块后会修改自旋变量参数,若被唤醒仍然没有抢到锁则进入Thread.onSpinWait()等待,减少park次数,若下次自旋还没有抢到锁则再次park,然后下下次自旋在进入Thread.onSpinWait()。以此交替执行下去,直到抢到锁为止。
        boolean interrupted = false, first = false;
        Node pred = null;                //入队列后的前驱节点

        /*
         * 自旋:
         *    检查是否第一次进入方法
         *    如果是确保头部节点稳定,否则需要确保是合法的前驱节点
         *    如果第一次进来或者节点没有入队列,尝试获取锁
         *    如果节点没有创建,则创建
         *    如果节点没有入队列,则首次尝试入队列 
         *    如果线程从park中被唤醒,再次自旋重试
         *    如果节点没有获取到锁且状态不是waiting,则设置为waiting
         *    都不满足则阻塞线程,线程被唤醒后清空waiting 状态,检查是否取消了等待 
         */
        //自旋中拿锁,失败就 cancel
        for (;;) {// 是第一次进来,就没要执行这个 if 了
            if (!first && (pred = (node == null) ? null : node.prev) != null && // node 节点是否已经在队列里了
                !(first = (head == pred))) {// 判断当前节点是不是队列中的第一个节点,说明有不止一个线程在等待
                if (pred.status < 0) {// 表示已经取消了的节点
                    cleanQueue();           // 清理队列中已经取消了的节点,清理完 continue 
                    continue;
                } else if (pred.prev == null) {//node 的前一个节点是占位节点了
                    Thread.onSpinWait();    //  确保当前线程序列化
                    continue;
                }
            }
            //当前节点的前一个节点是否为头节点或者还没有初始化占位节点即没有入队列
            if (first || pred == null) {
                boolean acquired;
                try {
                    /* 
                        这两个方法有子类实现判断是否抢到了锁例如非公平锁的tryAcquired(1);
                        如果status为0,则acquired则会变成true。将当前线程设置为独占并更新status为arg
                        如果status不为0,则acquired=false,说明被其它线程上锁了
                            
                    */
                    
                    if (shared)//若是共享锁则尝试获取
                        acquired = (tryAcquireShared(arg) >= 0);
                    else//若是独占锁则尝试获取
                        acquired = tryAcquire(arg);
                } catch (Throwable ex) {
                    //存在异常取消获取
                    cancelAcquire(node, interrupted, false);
                    throw ex;
                }
                if (acquired) {//获取到了锁
                    if (first) {// first 由(head == pred)赋值,此时说明是头结点即占位节点
                        node.prev = null; //头结点出队列,并设置当前node节点为新的头节点即新的占位节点,
                        head = node;
                        pred.next = null;
                        node.waiter = null;
                        if (shared)// 通知下一个共享节点
                            signalNextIfShared(node);
                        if (interrupted)// 已经拿到锁了,若中断,就直接中断线程
                            current.interrupt();
                    }
                    return 1;//返回1标签抢占到锁了这是这个方法的唯一结束点,其它分支始终都会死循环
                }
            }
            if (node == null) {// node 为 null,表示要往队列中添加新节点,这里只是给 node 创建一个新节点,然后自旋
                if (shared)
                    node = new SharedNode();
                else
                    node = new ExclusiveNode();
            } else if (pred == null) {          //第一次往队列中添加节点
                node.waiter = current;//设置waiter为当前线程
                Node t = tail;//尾部节点引用
                node.setPrevRelaxed(t); // 将node的prev指向tail 加入到了队列的末尾,后面需要将队列末尾指向node形成双向的队列
                if (t == null)//如果队列还未初始化,则先初始化,就是 new 一个空的独占节点,让 head 和 tail 指向它
                    tryInitializeHead();
                else if (!casTail(t, node))//casTail(t, node) 把 node 设置成尾节点
                    node.setPrevRelaxed(null);  // 如果tail更新失败。则node.prev=null则又会重新进入if进行更新,直到更新成功。
                else
                    t.next = node;//cas 成功将尾部节点next指向当前节点形成双向队列
            } else if (first && spins != 0) { //如果某个线程阻塞过了被唤醒了但再次没有抢到锁则为了减少 park 的次数,使用自旋等待让出cpu的时间片,每次自旋次数减一
                --spins;                        // 减少对等待的不公平
                Thread.onSpinWait();//让出cpu使用全和Thread.sleep(0)差不多的作用,但是Thread.onSpinWait();更高效,使用的是cpu指令
            } else if (node.status == 0) {//把节点状态改为 WAITING 才能 park
                node.status = WAITING;          // enable signal and recheck
            } else {//拿锁失败,park
                long nanos;
                //spins!=0则会调用Thread.onSpinWait();,让当前线程让出cpu的使用权,减少park次数
                spins = postSpins = (byte)((postSpins << 1) | 1);
                if (!timed)//不带超时的阻塞
                    LockSupport.park(this);
                else if ((nanos = time - System.nanoTime()) > 0L)
                    LockSupport.parkNanos(this, nanos);// //阻塞nanos纳秒
                else//如果时间不合法或者时间nanos<=0L 则break
                    break;
                node.clearStatus();//阻塞被唤醒则将status重新更新为0
                //如果被中断则interupted被赋值为true
                if ((interrupted |= Thread.interrupted()) && interruptible)
                    break;
            }
        }
       // 有异常、超时或中断,取消获取
        return cancelAcquire(node, interrupted, interruptible);
    }


    // 初始化队列
    private void tryInitializeHead() {
        Node h = new ExclusiveNode();
        if (U.compareAndSetReference(this, HEAD, null, h))
            tail = h;
    }
 

	// 通知下一个共享节点
    private static void signalNextIfShared(Node h) {
        Node s;
        //注意这里的h是占位节点next节点才是被阻塞的线程节点
        if (h != null && (s = h.next) != null && (s instanceof SharedNode) && s.status != 0) {
            s.getAndUnsetStatus(WAITING);  // 取消 WAITING 状态
            LockSupport.unpark(s.waiter);//唤醒被阻塞的线程
        }
    }

在这里插入图片描述
小结

  1. 对于新进来的线程, 只要没有入队列都会尝试获取锁tryAcquired(),获取到则先唤醒共享节点的下一个节点然后返回
  2. 根据不同的入参share创建新的节点:true为共享节点,false则为独占节点,并设置节点的waiter 属性为当前线程
  3. 未初始化队列则初始化,否则cas 该新节点node入队尾
  4. 如果某个线程阻塞过了被唤醒了但再次没有抢到锁则为了减少 park 的次数,使用自旋等待让出cpu的时间片,每次自旋次数减一
  5. 将node节点状态改为waiting 然后根据是否带超时进行park阻塞,阻塞的线程醒来会清除锁状态继续请求获取锁
  6. 如果在获取锁的过程中因中断,异常,则取消获取锁cancelAcquire(node, interrupted, interruptible) 并清理cleanQueue() 已经取消的线程节点

cleanQueue 清除队列

private void cleanQueue() {
    //这里需要自旋因为会存在多个线程访问导致CAS取消节点移除队列失败的情况,自旋确保每个取消获取锁的线程都能移除同步队列
        for (;;) {                               // restart point
            for (Node q = tail, s = null, p, n;;) { // (p, q, s) triples
                if (q == null || (p = q.prev) == null)//队列为空或者执行到了占位节点了,结束自旋
                    return;                      // end of list
                if (s == null ? tail != q : (s.prev != q || s.status < 0))
                    break;                       // inconsistent
                if (q.status < 0) {              // 需要取消的线程节点
                    //首次进来s==null 为true,将q的前一个节点p设置为尾部;否则将s的prev指针指向p;
                    //若cas成功了则q.prev== p 为true
                    if ((s == null ? casTail(q, p) : s.casPrev(q, p)) &&
                        q.prev == p) {
                        p.casNext(q, s);         //s为新的尾部节点,p的next指向s
                        if (p.prev == null)//p已经是占位节点,唤醒p的next线程
                            signalNext(p);
                    }
                    break;
                }
                // p.next!=q 说明已经有其他线程将尾部节点更改 则帮助完成形成链表
                if ((n = p.next) != q) {         
                    if (n != null && q.prev == p) {
                        p.casNext(n, q);
                        if (p.prev == null)
                            signalNext(p);
                    }
                    break;
                }
                s = q;
                q = q.prev;
            }
        }
    }
 

取消获取

private int cancelAcquire(Node node, boolean interrupted, boolean interruptible) {
        if (node != null) {
            // 将与node绑定的线程
          // 将node 的状态设置为已取消
            node.waiter = null;
            node.status = CANCELLED; 
            // 若node.prev != null则说明node已经被加入到等待队列
          // 此时由于node为CANCELLED状态,队列做一次清理
            if (node.prev != null)
                cleanQueue();
        }
        if (interrupted) {//如果线程被中断了
            if (interruptible)//取消中断
                return CANCELLED;//常量负数
            else
                Thread.currentThread().interrupt();// 将线程中断
        }
        return 0;//返回0
    }

释放锁

//独占锁 
public final boolean release(int arg) {
        if (tryRelease(arg)) {//子类实现
            signalNext(head);//唤醒下一个节点。head节点是占位节点,next节点才是被阻塞的线程节点
            return true;//到此next的节点已经出队列,next指向其下一个阻塞的线程节点
        }
        return false;
    }
//释放共享锁 
 public final boolean releaseShared(int arg) {
        if (tryReleaseShared(arg)) {//子类实现
            signalNext(head);
            return true;
        }
        return false;
    }
 private static void signalNext(Node h) {
        Node s;
        if (h != null && (s = h.next) != null && s.status != 0) {
            s.getAndUnsetStatus(WAITING);//取消等待设置为status=0
            LockSupport.unpark(s.waiter);//唤醒阻塞的线程节点
        }
    }

hasQueuedPredecessors

//是否有前驱节点在同步队列里,有说明等待队列里有其他线程正在获取锁,或者队列里就只有当前线程或者队列为空均返回false
//此方法使用在semaphore信号量的获取公平锁和ReentrantReadWriteLock读写锁中,其他地方未发现使用
 
public final boolean hasQueuedPredecessors() {
    Thread first = null; Node h, s;
     //头节点不为null 并且(头结点的next节点为null||next所对应的线程为null||next的前一个节点为null
    if ((h = head) != null && ((s = h.next) == null ||
                               (first = s.waiter) == null ||
                               s.prev == null)) 
        first = getFirstQueuedThread(); //需要重新获取队列里的最长等待的线程
    return first != null && first != Thread.currentThread();
}
 //获取队列里的最长等待的线程
 public final Thread getFirstQueuedThread() {
        Thread first = null, w; Node h, s;
        //头节点不为null 并且(头结点的next节点为null||next所对应的线程为null||next的前一个节点为null
        if ((h = head) != null && ((s = h.next) == null ||
                                   (first = s.waiter) == null ||
                                   s.prev == null)) {
            // traverse from tail on stale reads
            for (Node p = tail, q; p != null && (q = p.prev) != null; p = q)
                if ((w = p.waiter) != null)
                    first = w;
        }
        return first;
    }

待条件变量锁的获取

先看下AQS 中条件变量和同步队列的关系这样有助于理解流程,条件队列里的线程被唤醒后会被移入到同步队列里获取争抢锁的权利,

这一点可以在enqueue() 方法中看到

在这里插入图片描述

public final void await() throws InterruptedException {
            if (Thread.interrupted())
                throw new InterruptedException();
            ConditionNode node = new ConditionNode();
            int savedState = enableWait(node);//如果当前线程是持有锁线程,释放所有锁,并且把锁计数返回
            LockSupport.setCurrentBlocker(this); //设置当前对象为阻塞资源
            boolean interrupted = false, cancelled = false;
            while (!canReacquire(node)) {//自旋的方式判断是否正在队列里 ,在同步队列里说明不能再次尝试获取锁, false才停止自旋
                if (interrupted |= Thread.interrupted()) {//线程被中断了
                    if (cancelled = (node.getAndUnsetStatus(COND) & COND) != 0)//设置为取消状态
                        break;              // else interrupted after signal
                } else if ((node.status & COND) != 0) {
                    try {
                        ForkJoinPool.managedBlock(node);// 阻塞node节点的线程
                    } catch (InterruptedException ie) {
                        interrupted = true;//如果异常设置中断标记
                    }
                } else
                    Thread.onSpinWait();    //等待
            }
            //执行到这里说明线程被唤醒
            LockSupport.setCurrentBlocker(null);// 清除阻塞器
            node.clearStatus();//清除线程阻塞状态改为0
            acquire(node, savedState, false, false, false, 0L);//当前唤醒的线程重新竞争锁
            if (interrupted) {// 因异常导致中断标记为位真
                if (cancelled) {//且是取消状态
                    unlinkCancelledWaiters(node);//从等待队列中去除当前节点
                    throw new InterruptedException();
                }
                Thread.currentThread().interrupt();//发出中断请求
            }
        }

    //添加node节点到条件队列中并且释放锁
     private int enableWait(ConditionNode node) {
            if (isHeldExclusively()) {//是持有锁的线程节点
                node.waiter = Thread.currentThread();
                node.setStatusRelaxed(COND | WAITING);//设置状态值为3
                ConditionNode last = lastWaiter;
                if (last == null)//如果链表末尾节点为null,说明是空链表
                    firstWaiter = node;
                else
                    last.nextWaiter = node;
                lastWaiter = node;//当前节点插入到链表末尾
                int savedState = getState();
                if (release(savedState))//进入了条件队列中肯定需要释放锁,让其他线程拥有竞争锁的权利,注意这里直接是saveState表示释放锁的次数
                    return savedState;
            }
            node.status = CANCELLED; // 线程没有抢占到锁,更新节点的状态为取消状态
            throw new IllegalMonitorStateException();//因为当前线程不是持锁线程抛出非法监视器异常
        }
  //能否可以重新获取到锁,注意等待队列的节点是不带头节点即没有占位节点
   private boolean canReacquire(ConditionNode node) {
            // 如果此时的等待队列不止一个节点,优先是头节点可以再次尝试获取锁
            return node != null && node.prev != null && isEnqueued(node);
        }

//发生异常则从等待队列中去除当前节点
private void unlinkCancelledWaiters(ConditionNode node) {
            if (node == null || node.nextWaiter != null || node == lastWaiter) {
                ConditionNode w = firstWaiter, trail = null;
                while (w != null) {
                    ConditionNode next = w.nextWaiter;
                    if ((w.status & COND) == 0) {//找到需要移除的节点
                        w.nextWaiter = null;
                        if (trail == null)
                            firstWaiter = next;//重新保存队列头节点,因为异常的节点可能就是队列头节点,所以重新得到头节点
                        else
                            trail.nextWaiter = next;
                        if (next == null)
                            lastWaiter = trail;
                    } else
                        trail = w;
                    w = next;
                }
            }
        }

唤醒头节点signal

public final void signal() {
    ConditionNode first = firstWaiter;// 链表头
    if (!isHeldExclusively()) // 如果当前线程不是持有锁的对象 
        throw new IllegalMonitorStateException();// 抛非法监视器状态异常
    if (first != null)//只有获得了锁才能调用await方法,然后被另一个线程调用doSignal方法唤醒
        doSignal(first, false);//false唤醒一个
}

 private void doSignal(ConditionNode first, boolean all) {
            while (first != null) {
                ConditionNode next = first.nextWaiter;
                if ((firstWaiter = next) == null)// 将firstWaiter 赋值为下一个节点并判断如果下一个节点为空,说明到了链表的末尾节点
                    lastWaiter = null;//置空尾部节点
                if ((first.getAndUnsetStatus(COND) & COND) != 0) {
                    enqueue(first);//将条件队列头节点从等待队列移入同步队列,该头节点将被挂载到同步队列的尾部,从await方法处被唤醒拥有竞争锁的权利(调用了acquire), 但唤醒不代表一定能抢到锁,只是拥有竞争锁的权利而已;当然如果原来的尾部节点是取消获取锁的状态则优先唤醒此线程
                    if (!all)//是否唤醒所有节点,如果传入的是true则继续遍历链表,针对singAll方法
                        break;
                }
                first = next;
            }
  }

final void enqueue(Node node) {
        if (node != null) { // 节点不为空
            for (; ; ) {  // 自旋
                Node t = tail;      
                node.setPrevRelaxed(t);         // 将node节点的prev指向t 
                if (t == null)                  // 如果aqs队列为空需要创建一个头 
                    tryInitializeHead();        // 尝试初始一个占位节点
                else if (casTail(t, node)) {    // 将node入队到末尾,更新TAIL为node 
                    t.next = node;              // 因为t指向node 
                    if (t.status < 0)           // 如果原来的尾部节点是取消获取锁的状态则优先唤醒node刚入队列的线程
                        LockSupport.unpark(node.waiter); // 唤醒node线程
                    break;  
                }
            }
        }
    }
 

唤醒所有被Condition阻塞的线程

public final void signalAll() {
            ConditionNode first = firstWaiter; 
            if (!isHeldExclusively())// 当前线程对象不是持有锁线程则配移除
                throw new IllegalMonitorStateException();// 抛异常
            if (first != null)
                doSignal(first, true);// 进行唤醒所以条件队列的线程
        }
 

可中断的等待

public final void awaitUninterruptibly() {
            ConditionNode node = new ConditionNode();
            int savedState = enableWait(node);//入条件队列并且释放锁
            LockSupport.setCurrentBlocker(this); //设置当前对象为阻塞资源
            boolean interrupted = false;
            while (!canReacquire(node)) { //自旋的方式判断是否正在队列里 ,在同步队列里说明不能再次尝试获取锁, false才停止自旋
                if (Thread.interrupted())// 如果已经被中断了则更新interrupted = true;
                    interrupted = true;
                else if ((node.status & COND) != 0) {  
                    try {
                        ForkJoinPool.managedBlock(node);// 阻塞当前资源
                    } catch (InterruptedException ie) {
                        interrupted = true;
                    }
                } else
                    Thread.onSpinWait();    // awoke while enqueuing
            }
            LockSupport.setCurrentBlocker(null);// 清除阻塞资源
            node.clearStatus(); // 清除状态值
            acquire(node, savedState, false, false, false, 0L);
            if (interrupted)
                Thread.currentThread().interrupt();// 中断当前线程
        }

AQS队列和Condition队列的关系

在这里插入图片描述
Condition队列的线程会在被其他线程唤醒时从Condition移入到AQS队列,拥有竞争锁的权利(如果入队时的队尾线程锁的状态是取消状态则该入队列的线程优先唤醒)

与jdk11的区别

本质区别:jdk14 将锁的获取与释放不依赖于前驱节点的状态,根据当前节点能获取到锁则获取,否则入队列阻塞

jdk14 移除了waitStatus 属性,将锁的实现整合到aquiire()中,在线程没有入队列之前都会请求获取锁,然后根据需要创建不同的节点来入队列,并设置节点状态为waiting 再次自旋则被阻塞。说明入队列就被阻塞

jdk11 主要根据 当前入队列节点的前驱节点waitsStaus 属性的状态来判断当前线程节点是否阻塞,整个阻塞核心方法为shouldParkAfterFailedAcquire()。如果前驱节点不是头节点,或者前驱节点是头节点但当前节点获取锁失败,这时当前节点需要挂起,分三种情况:

  • 如果队列的前驱节点 waitsStaus =-1 说明前驱节点 还没有出队列当前节点需要阻塞;
  • 如果队列的前驱节点 waitsStaus >0 说明前驱节点点放弃了向队列里找满足waitsStaus =-1 的节点出队列,当前节点线程阻塞
  • 如果队列的前驱节点 waitsStaus <0 && waitsStaus !=-1 通过CAS操作设置前驱节点状态为SIGNAL=-1 ,当前节点线程阻塞

出现异常或者超时时间到会触发取消获取cancelAcquire:

  • 通过循环获取当前节点不为CANCELLED状态的前驱节点pred
  • 当前节点是tail节点,将pred节点置为tail,并cas 新的tail 的next=null
  • 如果当前节点的前驱节点pred不是头节点 同时 前驱节点的等待状态为SIGNAL(如果不是SIGNAL那就设置为SIGNAL) 且 前驱节点封装的线程不为NULL, 如果后继节点的等待状态不为CANCELLED,则通过CAS将前驱节点的后继指针指向当前节点的后继节点
  • 如果当前节点的前驱节点是头节点,则直接唤醒当前节点的后继节点,移除除当前节点

主要用到 AQS 的并发工具类

在这里插入图片描述

  • 10
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值