并发编程-AQS详解

解析:AbstractQueuedSynchronizer

同步等待队列(CLH)

AQS当中的同步等待队列也称CLH队列,CLH队列是Craig、Landin、Hagersten三人发明的一种基于双向链表数据结构的队列,是FIFO先入先出线程等待队列,Java中的CLH队列是原CLH队列的一个变种,线程由原自旋机制改为阻塞机制

  • head 指向的是一个null 线程,tail 指向最后一个线程Node。

在这里插入图片描述

Node节点

  • 持有线程对象的引用
  • 双向链表
  • 每个节点都有一个状态waitStatus
    • CANCELLED = 1; 节点被标记等待被剔除队列
    • SIGNAL = -1; 节点等待被唤醒
    • CONDITION = -2; 这个节点在条件同步等待队列中
    • PROPAGATE = -3;表示还有剩余的锁,可以获取。

获取锁的函数

acquire(),acquireQueued()互斥不响应中断。
LOCK

public final void acquire(int arg) {
// tryAcquire(arg) 这个需要自己去实现逻辑在LOCK中有分析
// 总结就是去尝试获取锁。
   if (!tryAcquire(arg) &&
   //如果没有获取锁,也就是tryAcquire=false的话会继续下面
   //addWaiter(Node.EXCLUSIVE) 就是将这个节点添加到同步等待队列中去
       acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
       selfInterrupt();
}

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)) {
            	//如果获取到锁,将当前节点设置为head节点,让原来的head 被gc。
                setHead(node);
                p.next = null; // help GC
                failed = false;
                return interrupted;
            }
            // p 是当前节点前一个节点。
            /*shouldParkAfterFailedAcquire(p, node) 这个函数是利
            用CAS自旋将pred的WaitStatus 设置为-1,也就是将自己设置为等待唤醒状态的节点。
			*/
            if (shouldParkAfterFailedAcquire(p, node) &&
            	//阻塞
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

acquireInterruptibly(), doAcquireInterruptibly(arg);

public final void acquireInterruptibly(int arg)
        throws InterruptedException {
        //如果线程是被中断不是队列首节点唤醒,抛异常 不会放入到CLH队列中去
    if (Thread.interrupted())
        throw new InterruptedException();
        //若不是被中的 那么尝试获得CHL锁。
    if (!tryAcquire(arg))
        doAcquireInterruptibly(arg);
}
private void doAcquireInterruptibly(int arg)
    throws InterruptedException {
    //创建一个CLH节点并添加到CHL队列中去
    final Node node = addWaiter(Node.EXCLUSIVE);
    boolean failed = true;
    try {
    //自旋
        for (;;) {
        //获取前一个节点,判断是不是Head之后的节点,也就是第一个可用节点
            final Node p = node.predecessor();
            //如果是首节点,尝试获取锁
            if (p == head && tryAcquire(arg)) {
            //成功,设置这个点为head,
                setHead(node);
                p.next = null; // help GC
                failed = false;
                return;
            }
            //失败
            //设置这个点为等待唤醒的点,并且静如等待状态,后续被唤醒后如果是中断唤醒的,就抛出异常。
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                throw new InterruptedException();
        }
    } finally {
        if (failed)
        //如果是中断唤醒的那么会将这个节点删除。并将异常抛出
            cancelAcquire(node);
    }
}

acquireShared(),doAcquireShared(),setHeadAndPropagate()

public final void acquireShared(int arg) {
//tryAcquireShared()尝试获取锁,如果返回<0说明现在共享的锁已经被取完。
    if (tryAcquireShared(arg) < 0)
    //入队列操作。
        doAcquireShared(arg);
}
private void doAcquireShared(int arg) {
	//将一个共享节点放入同步等待队列中。
   final Node node = addWaiter(Node.SHARED);
   boolean failed = true;
   try {
       boolean interrupted = false;
       //自旋
       for (;;) {
           final Node p = node.predecessor();
           //判断这个节点是不是首节点。
           if (p == head) {
           //如果是首节点,再次尝试去获取锁。
               int r = tryAcquireShared(arg);
               if (r >= 0) {
               //若获取到锁。
               //如果r>0 那么会设置这个点是一个可以传播的头节点并且可以传播(也就是还有锁)
                   setHeadAndPropagate(node, r);
                   p.next = null; // help GC
                   if (interrupted)
                       selfInterrupt();
                   failed = false;
                   return;
               }
           }
           //获取不到锁,或者不是首节点 进去阻塞状态等待被唤醒。
           if (shouldParkAfterFailedAcquire(p, node) &&
               parkAndCheckInterrupt())
               interrupted = true;
       }
   } finally {
       if (failed)
           cancelAcquire(node);
   }
}
private void setHeadAndPropagate(Node node, long propagate) {
   Node h = head; // Record old head for check below
   setHead(node);
	// 如果还有锁 会再去释放队列里面的节点
   if (propagate > 0 || h == null || h.waitStatus < 0 ||
       (h = head) == null || h.waitStatus < 0) {
       Node s = node.next;
       // 如果是共享节点 会让他下一个节点尝试去拿锁
       if (s == null || s.isShared())
           doReleaseShared();//重点
   }
}

释放锁的函数

release(),unparkSuccessor()
LOCK

public final boolean release(int arg) {
//tryRelease(arg)) 在LOCK中有说明。
    if (tryRelease(arg)) {
        Node h = head;
        //如果首节点不为空,并且不是0,也就是任何只要有状态都可以被尝试唤醒
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
        return true;
    }
    return false;
}

private void unparkSuccessor(Node node) {

    int ws = node.waitStatus;
    if (ws < 0)
    /*这里会尝试去将head的waitStatus的状态设置为0.个人感觉这个对这个是
    没有关系。因为如果后面会将这个节点抛弃或者重新设置-1。共享锁可能会使
    用,后面继续看源码。
	*/
        compareAndSetWaitStatus(node, ws, 0);

	
    Node s = node.next;
    //如果这个节点是CANCELLED 那么剔除这个节点。并且将唤醒几乎给下一个可唤醒的节点。
    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;
    }
    //如果队列中还有真实线程Node 可以将其唤醒。
    if (s != null)
        LockSupport.unpark(s.thread);
}

releaseShared(),releaseShared(),doReleaseShared()

public final boolean releaseShared(int arg) {
// 尝试释放共享锁,
    if (tryReleaseShared(arg)) {
    //如果成功释放
        doReleaseShared();
        return true;
    }
    return false;
}
//Semaphore 的实现
protected final boolean tryReleaseShared(int releases) {
   for (;;) {
       int current = getState();
       int next = current + releases;
       if (next < current) // overflow
           throw new Error("Maximum permit count exceeded");
           //自旋将State+releases 
       if (compareAndSetState(current, next))
           return true;
   }
}
private void doReleaseShared() {
//这个函数是去唤醒首节点
   for (;;) {
       Node h = head;
       //若队列不为空
       if (h != null && h != tail) {
           int ws = h.waitStatus;
           //若是可唤醒状态
           if (ws == Node.SIGNAL) {
           //自旋将这个节点设置不可可唤醒也就是已经要被我唤醒其他线程进不来了,如果失败就自旋,成功就 唤醒。
               if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                   continue;            // loop to recheck cases
               unparkSuccessor(h);
           }
           //若头节点的waitStatus是0,说明是在被唤醒过程,将这个点设置为PROPAGATE。
           //如果成功就会等待退出循环,并不会去唤醒了,并且告诉其他来的线程,还有锁可以获取
           //如果失败会去尝试唤醒新的首节点。
           else if (ws == 0 &&
                    !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
               continue;                // loop on failed CAS
       }
       //由于被唤醒了,那么头节点改变了就会退出循环。
       if (h == head)                   // loop if head changed
           break;
   }
}

条件等待队列

Condition是一个多线程间协调通信的工具类,使得某个,或者某些线程一起等待某个条件(Condition),只有当该条件具备时,这些等待线程才会被唤醒,从而重新争夺锁。
在这里插入图片描述

节点

  • Node firstWaiter 第一个节点
  • Node lastWaiter 最后一个节点

等待函数

await(),addConditionWaiter()

public final void await() throws InterruptedException {
   if (Thread.interrupted())
       throw new InterruptedException();
       //将当前线程放入条件队列中去。
   Node node = addConditionWaiter();
   //将当前State保存,并且释放掉
   int savedState = fullyRelease(node);
   int interruptMode = 0;
   //如果这个节点不在同步等待队列中
   while (!isOnSyncQueue(node)) {
   //将当前队列阻塞。
       LockSupport.park(this);
       if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
           break;
   }
   //当被加入到同步等待队列中后,会等待被唤醒,
   if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
       interruptMode = REINTERRUPT;
   if (node.nextWaiter != null) // clean up if cancelled
       unlinkCancelledWaiters();
   if (interruptMode != 0)
       reportInterruptAfterWait(interruptMode);
}

唤醒函数

Singnal() ,doSignal(),transferForSignal()

public final void signal() {
   if (!isHeldExclusively())
       throw new IllegalMonitorStateException();
       //获取条件队列的头节点
   Node first = firstWaiter;
   if (first != null)
       doSignal(first);
}

private void doSignal(Node first) {
   do {
   //若条件队列没了,设置为空
       if ( (firstWaiter = first.nextWaiter) == null)
           lastWaiter = null;
       first.nextWaiter = null;
       //遍历条件队列里面的节点执行transferForSignal()
   } while (!transferForSignal(first) &&
            (first = firstWaiter) != null);
}
final boolean transferForSignal(Node node) {
       //设置节点状态为 0 。如果失败,说明这个节点是cancelled,就不需要添加到同步等待队列中去了。
       if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
           return false;
        // 将节点添加到同步等待队列中
       Node p = enq(node);
       int ws = p.waitStatus;
      
       if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
           LockSupport.unpark(node.thread);
           //表示转移成功
       return true;
   }


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值