1

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

我的问题是:以线程安全的方式检查 aLogger在创建它之前是否存在并同时阻止其他线程无意创建它的最佳方法是什么Logger

背景:java.util.logging.Logger#getLogger(String, String)将(原子地,以线程安全的方式)查找一个已命名的对象(Logger如果存在),如果不存在则创建它。但就我而言,我想在开始进行一些复杂的定位/重新定位Logger之前检查是否已经存在。ResourceBundle然后,在 myResourceBundle设置好之后,我知道它是有效的,我想将其名称提供给Logger#getLogger(String, String)调用。

ResourceBundle只有当 aLogger确实不存在时,我才需要进行这种定位/重新设置。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 synchronizedLogger.class

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

感谢您的时间和关注!

4

2 回答 2

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);
    }
}
于 2012-07-03T18:33:33.703 回答
0

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

于 2012-07-03T18:16:53.773 回答