如何在创建之前检查java.util.logging.Logger是否已存在?

时间:2012-07-03 17:07:48

标签: java concurrency java.util.logging

我遇到了java.util.logging死锁,我怀疑这与我在创建它之前检查是否存在Logger的方式有关。

我的问题是:在创建Logger之前检查线程安全方式的最佳方法是什么,并阻止其他线程无意中创建Logger在此期间?

背景:java.util.logging.Logger#getLogger(String, String)将(以原子方式,以线程安全的方式)查找名为Logger(如果存在),并创建它(如果不存在)。但在我的情况下,我想在设置执行一些复杂的Logger定位/重新定位之前检查ResourceBundle是否已经存在。然后,在设置ResourceBundle之后,我知道它有效,我想将其名称提供给Logger#getLogger(String, String)来电。

我需要这个ResourceBundle定位/重新定位只有在Logger确实不存在时才会发生。我需要让这整个事情以原子方式发生,以便其他一些线程在我设置我的Logger#getLogger(String, String)时不会进行ResourceBundle调用 - 我不想要其他线程例如,潜入那里并使用不同的 ResourceBundle名称,从而使我的所有努力无效。

我的成语是这样的:

Logger logger = null;
final LogManager logManager = LogManager.getLogManager();
assert logManager != null;
synchronized (logManager) {
  // This method call finds the logger, but doesn't create one.
  logger = logManager.getLogger(myLoggerName);
  if (logger == null) {
    // no logger found; time to do expensive ResourceBundle lookup/parenting/etc.
    // ...time passes...
    logger = Logger.getLogger(myLoggerName, myResourceBundleNameIJustCalculated);
  }
}
assert logger != null;

这导致了僵局。

当我的代码被锁定在全局LogManager上时,另一个线程无意中执行了一个完全不相关的Logger.getLogger(name)调用,从而获得了对Logger.class对象的锁定Logger#getLogger(String, String)是一个staticsynchronized方法。)所以这个线程锁定Logger.class ...我的线程(如上所示)需要运行他的 Logger.getLogger(name, resourceBundleName)来电。在内部,Logger.getLogger(name)抓取全局LogManager上的锁定。瞧。死锁。

认为防止这种情况的方法是遵循以同一顺序获取所有锁的历史悠久的传统。据我所知,只需将我的 synchronized块与另一个 synchronized块一起包围 - 但这次是在Logger.class - 应该做诀窍。那似乎对吗?那就是:

synchronized (Logger.class) {
  synchronized (globalLogManager) {
    // Hypothesis: this grabs locks in the same order
    // that java.util.logging classes use.  Should prevent
    // deadlocks?
  }
}

感谢您的时间和关注!

2 个答案:

答案 0 :(得分:1)

不要锁定你不能控制的物体;这可能会导致问题。或者,您可以使用控制的其他对象,例如Map<String, Logger>。您可以像使用日志管理器一样使用普通java.util.HashMap<String, Logger>synchronized块。

// import java.util.*;
// import java.util.logging.*;
final static Map<String, Logger> loggers = new HashMap<String, Logger>();

Logger logger;
synchronized(loggers) {
    logger = loggers.get(myLoggerName);
    if (logger == null) {
        // ... your expensive computation ...
        logger = Logger.getLogger(myLoggerName, myResourceBundleName);
        loggers.put(myLoggerName, logger);
    }
}

答案 1 :(得分:0)

由于getLoggeraddLogger调用已经为您同步,我不认为同步该块会为您带来任何好处。我只想删除同步。