У меня есть код, из которого я пытаюсь получить экземпляр моего класса, поскольку я написал обертку вокруг 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;
}
И так я инициализирую его в своих других классах, где мне нужно использовать мой выше регистратор -
private static final ClientLogger s_logger = ClientLogger.getInstance(TestLogger.class);
Теперь, когда я запускаю свой инструмент статического анализа, он жалуется, как и в моем классе ClientLogger
-
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;
}
Ниже приводится способ инициализации моего экземпляра моего регистратора -
private static final ClientLogger s_logger = ClientLogger.getInstance(TestLogger.class);
Так безопасен ли мой код выше? Я делаю result == null
check, поскольку в первый раз его не будет на карте, поэтому мне нужно сделать новое значение, и из-за этого мне нужно удалить окончательный модификатор результата.
Что вам нужно - это Java 8
s_classLoggers.computeIfAbsent(klass, ClientLogger::new);
Это создаст объект, если это действительно необходимо.
Заметьте, что ClientLogger::new
- потрясающая короткая рука, так как она короткая для k → new ClientLogger(k)
что является коротким для
new Function<Class<?>, ClientLogger>() {
public ClientLogger apply(Class<?> k) {
return new ClientLogger(k);
}
}
и lambda даже не генерирует класс во время компиляции, хотя JVM может (и делает это на Java 8) создавать класс во время выполнения.
В противном случае вы можете обнаружить, что блокировка записи более безопасна.
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;
}