如何删除非原子使用check / put并使代码线程安全?

时间:2014-09-30 16:30:35

标签: 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_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检查,因为这是第一次,它不会出现在地图中,所以我需要为它创建一个新值,因此,我需要删除结果的最终修饰符

1 个答案:

答案 0 :(得分:3)

您需要的是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;
}