Java synchronized块中的意外状态

时间:2012-11-15 21:54:45

标签: java thread-safety openam

我正在调查的一些开源代码中出现以下情况。它使用一个synchronized块,它执行以下操作:

  1. 将私有布尔实例变量hasListener设置为true
  2. 调用一个方法,该方法使用一个大的try块进行一些wait(),它会在实例变量中存储任何异常
  3. 将hasListener设置为false
  4. 抛出以前存储的所有异常
  5. 在同一个类中还有另一个类似的方法,它完全相同。

    理论上,这应确保hasListener在您输入时始终为false。然而以某种方式,在底部抛出异常(请参阅注释标记//?),因为它进入方法并且hasListener为true。我已经检查过没有其他地方设置了hasListener,默认值为false。是否有任何可以想象的情况,在waitFirstMessage()底部抛出异常会阻止变量被设置为false?还有其他可能的情况吗?

    从日志中看,它看起来像是一个合法的异常(“超出完成操作的时间”),从那时起,异常(//?)就会经常抛出。

    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());
        }
    }
    

    修改

    好的,事实证明我的问题不正确。当前在生产中运行日志的版本比我正在查看的代码稍早,并且有人清楚地解决了这个问题。在以前的版本中,waitForMessage()方法抛出异常。这些正在中断waitFirstMessage(int msgId),然后hasListener永远不会被设置为false。宇宙再次有意义。

    非常感谢回复。现在我需要将此修复程序投入生产!

2 个答案:

答案 0 :(得分:0)

如果waitForMessage因抛出异常而突然完成,则无法访问hasListener = false。如果此代码旨在确保执行hasListener = false,则应将其放入finally块中。

答案 1 :(得分:0)

hasListener抛出的情况下,

waitForMessage将被设置为true。由于waitForMessage捕获了所有异常,因此恕我直言的情况就是抛出某些异常(某些其他Throwable),或者在m_exception的实例化期间抛出异常时的情况