AQS

AbstractQuenedSynchronizer抽象的队列式同步器。基于 自旋+CAS+volatile

AQS的核心思想是,如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并将共享资源设置为锁定状态,如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制AQS是用CLH队列锁实现的,即将暂时获取不到锁的线程加入到队列中。
CLH(Craig,Landin,and Hagersten)队列是一个虚拟的双向队列,虚拟的双向队列即不存在队列实例,仅存在节点之间的关联关系。
AQS是将每一条请求共享资源的线程封装成一个CLH锁队列的一个结点(Node),来实现锁的分配。

AQS框架 - 管理状态

  1. AQS内部维护属性:volatile int state(32位)``````state表示资源的可用状态
  2. state三种访问方式 getState()setState()compareAndSetState()
  3. AQS定义两种资源共享方式
    Exclusive: 独占,只有一个线程能执行,如ReetrantLock
    Share: 共享,多个线程可以同时执行,如SemaphoreCountDownLatchReadWriteLockCyclicBarrier
  4. AQS定义两种队列
    ● 同步等待队列
    ● 条件等待队列

基于模板方法模式提供了获取资源和释放资源的模板方法,至于具体线程等待队列的维护,AQS已经在顶层实现好了。自定义同步器实现的时候主要实现下面几种方法:

isHeldExclusively():该线程是否正在独占资源。只有用到condition才需要去实现它。
tryAcquire(int):独占方式。尝试获取资源,成功则返回true,失败则返回false。
tryRelease(int):独占方式。尝试释放资源,成功则返回true,失败则返回false。
tryAcquireShared(int):共享方式。尝试获取资源。负数表示失败;0表示成功,但没有剩余可用资源;正数表示成功,且有剩余资源。
tryReleaseShared(int):共享方式。尝试释放资源,如果释放后允许唤醒后续等待结点返回true,否则返回false。

Java中断机制

Java中断机制是一种协作机制,通过 Thread.interrupt()并不能直接中断一个线程,只是将中断标志设置为true,需要被中断的线程自己去处理中断。

interrupt(): 将中断标记位设置为true。

isInterrupted(): 获取中断标记位。

interrupted():获取中断标记位,并将标记位重置。

throw InterruptException: 抛出该异常的同时,会重置中断标志位。

公平锁FairSync(ReentrantLock)

AbstractQueuedSynchronizer.acquire(int arg)

此方法是独占模式下线程获取共享资源的顶层入口。如果获取到资源,线程直接返回,否则进入等待队列,直到获取到资源为止,且整个过程忽略中断的影响。

/**
 * 以独占模式获取,忽略中断。通过至少调用一次tryAcquire来实现,成功时返回。
 * 否则,线程将进入队列,可能会反复阻塞和解除阻塞,调用tryAcquire直到成功。这个方法可以用来实现Lock.lock   *方法
 * 1、tryAcquire()尝试直接去获取资源,如果成功则直接返回(这里体现了非公平锁,每个线程获取锁时会尝试直接抢占加塞一次,而CLH队列中可能还有别的线程在等待)
 * 2、addWaiter()将该线程加入等待队列的尾部,并标记为独占模式
 * 3、acquireQueued()使线程阻塞在等待队列中获取资源,一直获取到资源后才返回。如果在整个等待过程中被中断过,则返回true,否则返回false
 * 4、如果线程在等待过程中被中断过,它是不响应的。只是获取资源后才再进行自我中断selfInterrupt(),将中断补上
 */
public final void acquire(int arg) {
    // 尝试加锁 
    // 加锁失败进入队列
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

AbstractQueuedSynchronizer.tryAcquire(int arg)

模板方法。尝试去获取独占资源。如果获取成功,则直接返回true,否则直接返回false。

AQS只是一个框架,具体资源的获取/释放方式交由自定义同步器去实现

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

ReentrantLock.tryAcquire(int acquires)

ReentrantLock独占锁实现

protected final boolean tryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    // 无锁状态
    if (c == 0) {
        // 如果当前线程之前有一个队列线程,则为true;如果当前线程位于队列头部或队列为空,则为false
        if (!hasQueuedPredecessors() &&
            // cas加锁
            compareAndSetState(0, acquires)) {
            // 设置工作线程
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    // 可重入锁实现
    // state累加,tryRelease的时候递减
    else if (current == getExclusiveOwnerThread()) {
        int nextc = c + acquires;
        if (nextc < 0)
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
}

AbstractQueuedSynchronizer.addWaiter(Node mode)

用于将当前线程加入到等待队列的队尾,并返回当前线程所在的结点

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入队
    enq(node);
    return node;
}

AbstractQueuedSynchronizer.enq(final Node node)

将node加入队尾

private Node enq(final Node node) {
    // CAS + 自旋 ,直到入队成功,cas 修改被volatile修饰的队列属性 tail、head 
    for (;;) {
        Node t = tail;
        // 队列为空,创建一个空的标志结点作为head结点,并将tail也指向它。
        // 第一次进来初始化第一个空节点,第二次自旋进来走else
        if (t == null) { // Must initialize
            if (compareAndSetHead(new Node()))
                tail = head;
        } 
        // 正常将节点插入到尾部,修改节点prev、next和队列的tail
        else {
            node.prev = t;
            if (compareAndSetTail(t, node)) {
                t.next = node;
                return t;
            }
        }
    }
}

AbstractQueuedSynchronizer.acquireQueued(final Node node, int arg)

通过 tryAcquire()addWaiter(),该线程获取资源

1、成功:返回。

2、失败:进入等待状态休息,直到其他线程彻底释放资源后唤醒自己

final boolean acquireQueued(final Node node, int arg) {
    //标记是否成功拿到资源
    boolean failed = true;
    try {
        //标记等待过程中是否被中断过
        boolean interrupted = false;
        // 自旋
        for (;;) {
            // 拿到prev前置节点
            final Node p = node.predecessor();
            // 如果前置节点是head,标识处于队列的第二位,为避免LockSupport.park线程切换的资源浪费,直接尝试去获取资源,
            // 有可能是节点唤醒的,也有可能被interrupt了
            // 如果获取资源成功,将当前节点设为head,抛弃之前的head
            if (p == head && tryAcquire(arg)) {
                setHead(node);
                p.next = null; // help GC
                // 成功获取资源
                failed = false;
                return interrupted;
            }
            //如果自己可以休息了,就通过park()进入waiting状态,直到被unpark()。如果不可中断的情况下被中断了,那么会从park()中醒过来,发现拿不到资源,从而继续进入park()等待。
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                //如果等待过程中被中断过,哪怕只有那么一次,就将interrupted标记为true
                interrupted = true;
        }
    } finally {
        // 如果等待过程中没有成功获取资源(如timeout,或者可中断的情况下被中断了),那么取消结点在队列中的等待。
        if (failed)
            cancelAcquire(node);
    }
}

AbstractQueuedSynchronizer.shouldParkAfterFailedAcquire(Node pred, Node node)

用于状态检查,判断前置节点是否是正常状态,往前遍历,知道找到一个活跃的节点。

整个流程中,如果前驱结点的状态不是SIGNAL,那么自己就不能安心去休息,需要去找个安心的休息点,同时可以再尝试下看有没有机会轮到自己拿号。

private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
    // 拿到前置节点等待状态
    int ws = pred.waitStatus;
    // 前置节点waitStatus是SIGNAL,为活跃节点
    if (ws == Node.SIGNAL)
        /*
         * This node has already set status asking a release
         * to signal it, so it can safely park.
         */
        return true;
     前置节点waitStatus是CANCELLED,往前遍历,直到找到一个SIGNAL
    if (ws > 0) {
        /*
         * Predecessor was cancelled. Skip over predecessors and
         * indicate retry.
         */
        do {
            node.prev = pred = pred.prev;
        } while (pred.waitStatus > 0);
        pred.next = node;
    } else {
        /*
         * waitStatus must be 0 or PROPAGATE.  Indicate that we
         * need a signal, but don't park yet.  Caller will need to
         * retry to make sure it cannot acquire before parking.
         */
        // 直接将状态改为SIGNAL
        compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
    }
    return false;
}

AbstractQueuedSynchronizer.parkAndCheckInterrupt()

让线程进入waiting状态。等待前置节点unpark去唤醒。在此状态下,有两种途径可以唤醒该线程:1)被unpark();2)被interrupt()

private final boolean parkAndCheckInterrupt() {
	//调用park()使线程进入waiting状态
    LockSupport.park(this);
    //如果被唤醒,检查是不是被中断的。
    return Thread.interrupted();
}

AbstractQueuedSynchronizer.cancelAcquire(Node node)

此方法主要清除当前节点状态,并将当前节点出队。

private void cancelAcquire(Node node) {
    // Ignore if node doesn't exist
    if (node == null)
        return;
	// node不再关联到任何线程
    node.thread = null;

    // Skip cancelled predecessors
    // 跳过被cancel的前继node,找到一个有效的前继节点pred
    Node pred = node.prev;
    while (pred.waitStatus > 0)
        node.prev = pred = pred.prev;

    // predNext is the apparent node to unsplice. CASes below will
    // fail if not, in which case, we lost race vs another cancel
    // or signal, so no further action is necessary.
    Node predNext = pred.next;

    // Can use unconditional write instead of CAS here.
    // After this atomic step, other Nodes can skip past us.
    // Before, we are free of interference from other threads.
    // 将node的waitStatus置为CANCELLED
    node.waitStatus = Node.CANCELLED;

    // If we are the tail, remove ourselves.
    // 如果node是tail,更新tail为pred,并使pred.next指向null
    if (node == tail && compareAndSetTail(node, pred)) {
        compareAndSetNext(pred, predNext, null);
    } else {
        // If successor needs signal, try to set pred's next-link
        // so it will get one. Otherwise wake it up to propagate.
        int ws;
        // 如果node既不是tail,又不是head的后继节点
        // 则将node的前继节点的waitStatus置为SIGNAL
        // 并使node的前继节点指向node的后继节点
        // 跳过当前node节点
        if (pred != head &&
            ((ws = pred.waitStatus) == Node.SIGNAL ||
             (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
            pred.thread != null) {
            Node next = node.next;
            if (next != null && next.waitStatus <= 0)
                compareAndSetNext(pred, predNext, next);
        } else {
            // 如果node是head的后继节点,则直接唤醒node的后继节点
            unparkSuccessor(node);
        }

        node.next = node; // help GC
    }
}

AbstractQueuedSynchronizer.release(int arg)

此方法是独占模式下线程释放共享资源的顶层入口。它会释放指定量的资源,如果彻底释放了(即state=0),它会唤醒等待队列里的其他线程来获取资源

public final boolean release(int arg) {
    // 
    if (tryRelease(arg)) {
        Node h = head;
        if (h != null && h.waitStatus != 0)
            //唤醒等待队列里的下一个线程
            unparkSuccessor(h);
        return true;
    }
    return false;
}

AbstractQueuedSynchronizer.tryRelease(int arg)

跟tryAcquire()一样,这个方法是需要独占模式的自定义同步器去实现的,处理可重入

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

AbstractQueuedSynchronizer.unparkSuccessor(Node node)

用于唤醒等待队列中下一个线程,即用unpark()唤醒等待队列中最前边的那个未放弃线程

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;
    // 如果后置节点为空或者waitStatus为CANCELLED
    if (s == null || s.waitStatus > 0) {
        s = null;
        // 从后往前找一个,找到最前面的可用的节点,即waitStatus <= 0
        for (Node t = tail; t != null && t != node; t = t.prev)
            if (t.waitStatus <= 0)
                s = t;
    }
    // 唤醒下一节点
    if (s != null)
        LockSupport.unpark(s.thread);
}

AbstractQueuedSynchronizer.acquireShared(int arg)

共享模式下线程获取共享资源的顶层入口。它会获取指定量的资源,获取成功则直接返回,获取失败则进入等待队列,直到获取到资源为止。

tryAcquireShared()需要自定义同步器去实现

public final void acquireShared(int arg) {
    // 获取资源,如果小于0,代表没有可用资源,进入等待队列
    if (tryAcquireShared(arg) < 0)
        doAcquireShared(arg);
}
一节点
    if (s != null)
        LockSupport.unpark(s.thread);
}

AbstractQueuedSynchronizer.acquireShared(int arg)

共享模式下线程获取共享资源的顶层入口。它会获取指定量的资源,获取成功则直接返回,获取失败则进入等待队列,直到获取到资源为止。

tryAcquireShared()需要自定义同步器去实现

public final void acquireShared(int arg) {
    // 获取资源,如果小于0,代表没有可用资源,进入等待队列
    if (tryAcquireShared(arg) < 0)
        doAcquireShared(arg);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值