首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为什么`h`的参数会在AbstractQueuedSynchronizer中两次判断是否为空?

为什么`h`的参数会在AbstractQueuedSynchronizer中两次判断是否为空?
EN

Stack Overflow用户
提问于 2020-07-07 15:37:28
回答 2查看 78关注 0票数 3

最近我在学习concurrency.When,我对Semaphore有了更多的了解,我有一些问题。

这是JDK1.8中AbstractQueuedSynchronizer的代码(第727行):

代码语言:javascript
复制
private void setHeadAndPropagate(Node node, int 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();
        }
    }

为什么这里两次判断h是否为空?什么时候h可以为null?我认为它们都不能为空。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2020-07-07 19:45:15

因为头节点是动态移动的,所以从两个方面调用doReleaseShared()

1.持有锁的线程调用release(),然后执行doReleaseShared()

2.有人线程执行acquire(),preNode是头获取锁成功的锁,然后执行doReleaseShared()

考虑以下可能的执行顺序:

下面是一些节点:head->init node ->node1->node2

有人释放了锁,然后唤醒了node1,node1从unpark中恢复,(节点1的线程名为Thread1,节点2的线程名为Thread2...)Node1的preNode是head,获取成功,permit是一个点。

代码语言:javascript
复制
private void doAcquireSharedInterruptibly(int arg)
        throws InterruptedException {
        final Node node = addWaiter(Node.SHARED);
        boolean failed = true;
        try {
            for (;;) {
                final Node p = node.predecessor();
                if (p == head) {
                    int r = tryAcquireShared(arg);//success , r=0;
                    if (r >= 0) { //true
                        setHeadAndPropagate(node, r);
                        p.next = null; // help GC
                        failed = false;
                        return;
                    }
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    throw new InterruptedException();
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

而在执行setHead(node)之后Thread1暂时挂起。

代码语言:javascript
复制
private void setHeadAndPropagate(Node node, int propagate) {
        Node h = head; // Thread2 come here and the Thread1 execute continue
        setHead(node);

        //Note: this point,the setHead(node) is done,but time slice 
        //      exhaustion,Thread1 is temporarily suspended 
                     
       
        if (propagate > 0 || h == null || h.waitStatus < 0 ||
            (h = head) == null || h.waitStatus < 0) {
            Node s = node.next;
            if (s == null || s.isShared())
                doReleaseShared();
        }
    }

另一个线程释放lock.current头是node1,CAS设置节点1的waitStatus==0,唤醒node2。

node2 preHead为node1并获取锁success.then execute setHeadAndPropagate()方法。

碰巧Node node = head和Thread2还在继续...

代码语言:javascript
复制
if (propagate > 0 || h == null || h.waitStatus < 0 ||
            (h = head) == null || h.waitStatus < 0) {
            Node s = node.next;
            if (s == null || s.isShared())
                doReleaseShared();
        }
//return immediate ,because 
//to old head
//propagate=0 false
//h==null fase
//h.waitStatus=0 false
//to new head node1
//propagate=0 false
//h==null fase
//h.waitStatus=0 false

所以让我们回到过去

代码语言:javascript
复制
 private void doAcquireSharedInterruptibly(int arg)
        throws InterruptedException {
        final Node node = addWaiter(Node.SHARED);
        boolean failed = true;
        try {
            for (;;) {
                final Node p = node.predecessor();
                if (p == head) {
                    int r = tryAcquireShared(arg);
                    if (r >= 0) {
                        setHeadAndPropagate(node, r);
                        p.next = null; // help GC //node1 could be GC!!!
                        failed = false;
                        return;
                    }
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    throw new InterruptedException();
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

最后,我们回到Thread2,Thread2继续。

当node2判断setHeadAndPropagate()方法行990中的if(...)时。

可能会发生h==null。所以doReleaseShared()

摘要:作为您的请求,第一个h表示旧的头部,它可以是null(特殊情况,时间切片导致的线程调度),但随后的(h=head==null)不能是null,因为它是当前的Thread2,这意味着当前的alive.you可以将其视为常规检查,请不要介意。

票数 1
EN

Stack Overflow用户

发布于 2020-07-07 15:43:52

第一次检查head (Node h = head),下次检查-可能是node (setHead(node))。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/62770269

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档