简体   繁体   English

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

[英]How can I check to see if a java.util.logging.Logger already exists before creating it?

I am running into a java.util.logging deadlock that I suspect is related to the way that I am checking for the existence of a Logger before (possibly) creating it. 我遇到一个java.util.logging死锁,我怀疑这与在(可能)创建Logger之前检查Logger是否存在的方式有关。

My question is: What is the best way to check—in a threadsafe manner—for a Logger 's existence before creating it, and to block some other thread's inadvertent creation of that Logger in the meantime? 我的问题是: 在创建Logger之前,以线程安全的方式检查Logger的存在并阻止其他一些线程无意中创建Logger的最佳方法是什么?

Background: java.util.logging.Logger#getLogger(String, String) will (atomically, in a thread-safe manner) look up a named Logger if one exists, and create it if it doesn't. 背景: java.util.logging.Logger#getLogger(String, String)将(以线程安全的方式,从原子上)查找一个命名的Logger如果存在),如果不存在则创建一个。 But in my case, I want to check to see if the Logger is there already before setting about doing some complicated ResourceBundle locating/reparenting. 但是就我而言,我想检查一下Logger是否已经存在,然后再设置一些复杂的ResourceBundle定位/重定位。 Then, after my ResourceBundle is set up, and I know it's valid, I want to supply its name to the Logger#getLogger(String, String) call. 然后,在设置ResourceBundle之后,我知道它是有效的,我想将其名称提供给Logger#getLogger(String, String)调用。

I need this ResourceBundle locating/reparenting to happen only if indeed a Logger does not exist. 只有在确实不存在Logger情况下,我才需要进行ResourceBundle定位/重定位。 I need to have this whole thing happen atomically so that some other thread doesn't do a Logger#getLogger(String, String) call while I'm in the middle of setting up my ResourceBundle —I don't want the other thread to sneak in there and use a different ResourceBundle name, for example, thus invalidating all my hard work. 我需要使整个事情自动发生,以便在我设置ResourceBundle的过程中,其他某个线程不执行Logger#getLogger(String, String)调用—我不希望其他线程例如,潜入那里并使用其他 ResourceBundle名称,从而使我所有的辛苦工作变得无效。

The idiom I had was something like this: 我的成语是这样的:

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;

This resulted in deadlock. 这导致了死锁。

While my code was locked on the global LogManager , another thread innocently executing a totally unrelated Logger.getLogger(name) call thereby acquired the lock on the Logger.class object ( Logger#getLogger(String, String) is a static and synchronized method.) So this thread has the lock on Logger.class ...which my thread (as you can see above) needs in order to run his Logger.getLogger(name, resourceBundleName) call. 当我的代码锁定在全局LogManager ,另一个线程无辜地执行了完全不相关的Logger.getLogger(name)调用,从而获得了Logger.class对象的锁定( Logger#getLogger(String, String)是一种staticsynchronized方法。 )因此,该线程具有Logger.class的锁...我的线程(如您在上面看到的)需要此锁才能运行他的 Logger.getLogger(name, resourceBundleName)调用。 Internally, Logger.getLogger(name) grabs the lock on the global LogManager . 在内部, Logger.getLogger(name)获取全局LogManager的锁。 Voila. Deadlock. 僵局。

I think that the way to prevent this is to follow the time-honored tradition of acquiring all locks in the same order. 认为防止这种情况的方法是遵循悠久的传统,即以相同的顺序获取所有锁。 From what I can tell, simply surrounding my synchronized block with another synchronized block—but this time on Logger.class —should do the trick. 从我所知道的,只是周围的synchronized块与另一个 synchronized块但这次Logger.class -should做的伎俩。 Does that seem right? 看起来对吗? That is: 那是:

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

Thanks for your time and attention! 感谢您的时间和关注!

Do not lock an object that you do not control; 请勿锁定您无法控制的对象; this can lead to problems. 这可能会导致问题。 Alternatively, you could use a different object that you control, such as a Map<String, Logger> . 或者,您可以使用控制的其他对象,例如Map<String, Logger> You can use a normal java.util.HashMap<String, Logger> along with a synchronized block in the same way you're using the log manager. 您可以使用普通的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);
    }
}

Since it the getLogger and addLogger calls are already synchronized for you, I don't think synchronizing that block is buying you anything. 因为它已经为您同步了getLoggeraddLogger调用,所以我认为同步该块不会给您带来任何addLogger I would just remove the synchronization. 我只是删除同步。

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

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