[英]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)
是一种static
且synchronized
方法。 )因此,该线程具有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. 因为它已经为您同步了
getLogger
和addLogger
调用,所以我认为同步该块不会给您带来任何addLogger
。 I would just remove the synchronization. 我只是删除同步。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.