简体   繁体   English

Java同步块内的意外状态

[英]Unexpected state inside Java synchronized block

I've got the following situation in some open source code I'm investigating. 我正在研究的某些开放源代码中有以下情况。 It is using a synchronised block which does the following: 它使用同步块执行以下操作:

  1. sets a private boolean instance variable hasListener to true 将私有布尔实例变量hasListener设置为true
  2. calls a method which does some wait()ing with a big try block around it which stores any exceptions inside an instance variable 调用一个方法,该方法在其周围有一个很大的try块来执行wait()处理,该块将任何异常存储在实例变量内
  3. sets hasListener to false 将hasListener设置为false
  4. throws any exceptions previously stored 抛出先前存储的任何异常

There is another similar method in the same class which does exactly the same thing. 同一类中还有另一种类似的方法,它们执行的功能完全相同。

In theory this should ensure that hasListener is only ever false when you enter. 从理论上讲,这应该确保hasListener仅在您输入时为false。 Yet somehow , an exception is being thrown at the bottom (see comment marked //?) because it enters the method and hasListener is true. 然而, 不知何故 ,底部将引发异常(请参见标记为//?的注释),因为它进入方法并且hasListener为true。 I've checked that there are no other places where hasListener is set, and the default value is false. 我检查过没有其他地方设置hasListener,并且默认值为false。 Are there any conceivable situations where throwing an exception at the bottom of waitFirstMessage() would prevent the variable being set back to false? 是否有任何可能的情况,在waitFirstMessage()的底部引发异常会阻止将变量设置回false? Any other possible situations? 还有其他可能的情况吗?

From the logs, it looks like a legitimate exception occurs ("Time to complete operation exceeded") and from that point on the exception (//?) is thrown fairly often. 从日志中看,似乎发生了合法的异常(“超出了完成操作的时间”),并且从那时开始,异常(//?)经常被抛出。

protected void waitFirstMessage (int msgId) throws LDAPException {
    synchronized (this) {
        if (!hasListener) {
            hasListener = true;
            while ((request != null) && (request.id == msgId) &&
                (m_exception == null) && (response == null)) {
                waitForMessage();
            }        
            hasListener = false;
            // Network exception occurred ?
            if (m_exception != null) {
                LDAPException ex = m_exception;
                m_exception = null;
                throw ex;
            }
        } else {
            //?
            throw new LDAPException();
        }
    }
}

 private void waitForMessage () throws LDAPException {
    try {
        if (request.timeToComplete > 0) {
            long timeToWait = request.timeToComplete -
                System.currentTimeMillis();
            if (timeToWait > 0) {
                wait(timeToWait);
                if (notified) {
                    notified = false;
                } else if (request.timeToComplete < System.currentTimeMillis()) {
                    // Spurious wakeup before timeout.
                    return;
                } else {
                    request = null;
                    m_exception = new LDAPException(
                        "Time to complete operation exceeded",
                        LDAPException.LDAP_TIMEOUT);
                }
            } else {
                request = null;
                m_exception = new LDAPException(
                    "Time to complete operation exceeded",
                    LDAPException.LDAP_TIMEOUT);
            }
        } else {
            wait();
            notified = false;
        }
    } catch (InterruptedException e) {
        m_exception = new LDAPInterruptedException("Interrupted LDAP operation");
    } catch (Exception e) {
      m_exception = new LDAPException("Unexpected exception while waiting for response",
          LDAPException.OTHER, e.getMessage());
    }
}

EDIT 编辑

Ok, it turns out that my question was incorrect. 好的,事实证明我的问题是不正确的。 The version currently running in production where the logs were coming from is slightly earlier than the code I was looking at and somebody has clearly addressed this very problem. 当前在生产环境中运行的版本(日志来自何处)比我正在查看的代码稍早,并且有人显然已经解决了这个问题。 In the previous version the waitForMessage() method was throwing exceptions. 在以前的版本中waitForMessage() 方法抛出异常。 These were interrupting waitFirstMessage(int msgId) and then hasListener was never being set to false. 它们正在中断waitFirstMessage(int msgId) ,然后hasListener从未设置为false。 The universe makes sense again. 宇宙再次有意义。

Thanks a lot for the replies. 非常感谢您的答复。 Now I need to get this fix into production! 现在,我需要将此修复程序投入生产!

If waitForMessage completes abruptly by throwing an exception, hasListener = false will not be reached. 如果waitForMessage通过引发异常突然完成,则将不会到达hasListener = false If this code was meant to ensure that hasListener = false is executed, it should have placed that into a finally block. 如果此代码旨在确保已执行hasListener = false ,则应将其放入finally块中。

hasListener would be left set to true in the case where waitForMessage throws. hasListener将留待设置为true在的情况下waitForMessage抛出。 Since waitForMessage catches all Exceptions, this could IMHO just be the case when something gets thrown that is not an exception (some other Throwable ), or if an exception is thrown during instatiation of m_exception . 由于waitForMessage捕获了所有异常,因此,当抛出不是异常(某些Throwable )的异常或在m_exception实例化期间引发异常时,恕我直言。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM