Java 如何消除check/put的非原子用法并使代码线程安全?
我有一段代码,我试图从中获取类的实例,因为我编写了一个关于Java 如何消除check/put的非原子用法并使代码线程安全?,java,multithreading,atomic,static-analysis,Java,Multithreading,Atomic,Static Analysis,我有一段代码,我试图从中获取类的实例,因为我编写了一个关于java.util.logging.Logger的包装器 下面是我的ClientLogger类中的代码片段- private static final Map<Class<?>, ClientLogger> s_classLoggers = new ConcurrentHashMap<Class<?>, ClientLogger>(); final private Logger m_log
java.util.logging.Logger
的包装器
下面是我的ClientLogger
类中的代码片段-
private static final Map<Class<?>, ClientLogger> s_classLoggers = new ConcurrentHashMap<Class<?>, ClientLogger>();
final private Logger m_logger;
private ClientLogger(final Class<?> caller) {
m_logger = Logger.getInstance(caller);
}
public static ClientLogger getInstance(final Class<?> klass) {
final ClientLogger result;
if (s_classLoggers.containsKey(klass)) {
result = s_classLoggers.get(klass);
} else {
result = new ClientLogger(klass);
s_classLoggers.put(klass, result);
}
return result;
}
Non-atomic use of check/put on this line s_classLoggers.put(klass, result);
现在,当我运行静态分析工具时,它正在抱怨,就像我的ClientLogger
类一样-
private static final Map<Class<?>, ClientLogger> s_classLoggers = new ConcurrentHashMap<Class<?>, ClientLogger>();
final private Logger m_logger;
private ClientLogger(final Class<?> caller) {
m_logger = Logger.getInstance(caller);
}
public static ClientLogger getInstance(final Class<?> klass) {
final ClientLogger result;
if (s_classLoggers.containsKey(klass)) {
result = s_classLoggers.get(klass);
} else {
result = new ClientLogger(klass);
s_classLoggers.put(klass, result);
}
return result;
}
Non-atomic use of check/put on this line s_classLoggers.put(klass, result);
所以我修改了上面的代码,使其线程安全-
private static final ConcurrentHashMap<Class<?>, ClientLogger> s_classLoggers = new ConcurrentHashMap<Class<?>, ClientLogger>();
public static ClientLogger getInstance(final Class<?> klass) {
ClientLogger result;
result = s_classLoggers.putIfAbsent(klass, new ClientLogger(klass));
// is below line thread safe and efficient?
if (result == null) {
result = new ClientLogger(klass);
}
return result;
}
那么我上面的代码是线程安全的吗?我正在做
result==null
检查,因为这是第一次,它不会出现在映射中,所以我需要为它创建一个新的值,因此,我需要删除result的最后一个修饰符。您需要的是Java 8
s_classLoggers.computeIfAbsent(klass, ClientLogger::new);
这将仅在确实需要时创建对象
注意,ClientLogger::new
是惊人的速记,因为它是k->newclientlogger(k)
的缩写,它是
new Function<Class<?>, ClientLogger>() {
public ClientLogger apply(Class<?> k) {
return new ClientLogger(k);
}
}
新功能考虑一下它是否有效。这将为getInstance的每次调用创建一个ClientLogger,即使不存在。还要考虑一下它是否有意义。==null的情况怎么可能发生,如果发生了,为什么用一个未存储在映射中的局部变量初始化结果会有帮助?@SimonFischer我想你是对的,但是对于null的情况,当我试图获取logger的实例时,它将第一次发生初始化,由于putIfAbsent不存在时将返回null,因此它将作为null出现。有更好的方法吗?你是对的,我本来打算在收到你的回复的那一刻纠正我自己关于空的情况:-)但在这种情况下,为结果分配ClientLogger的新实例而不将其添加到映射中仍然是毫无意义的,因为在这种情况下,您返回的内容与地图中的内容不同。如果映射中没有以前的值,则要将新创建的实例指定给结果。但即使这样,如果存在现有的ClientLogger(),您也会希望避免创建新的ClientLogger()。相关:链接的解决方案很好,因为它在决定创建新实例之前就已经存在了。不幸的是,到目前为止,我还没有Java 8。在Java 7中还有其他方法可以做到吗?@user2809564我已经更新了我的答案。如果很少使用锁,锁也不错。我假设你的缓存有足够高的命中率,一旦代码变热,它很少添加记录器。是的,一旦代码变热,它应该是缓存,当我尝试你的建议时,它在synchronized
上给了我错误?可能有什么遗漏了。@user2809564是的,在美国和Java,它是同步的
而不是同步的
,就像在英国英语中一样。是的,我自己能够纠正这个问题。最后一个问题,assylias给我指出了一个不使用synchronized的。你认为这对我来说也是另一种解决方案吗?
public static ClientLogger getInstance(final Class<?> klass) {
ClientLogger result = s_classLoggers.get(klass);
if (result != null)
return result; // fast path
// slow, but rare path
synchronized (s_classLoggers) {
result = s_classLoggers.get(klass);
if (result == null)
s_classLoggers.put(klass, result = new ClientLogger(klass));
}
return result;
}