2014-09-30 27 views
1

我有一个代码,我试图让我的班级的实例,因为我已经写了一个包装在java.util.logging.Logger如何删除check/put的非原子使用并使代码线程安全?

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

+0

想想它是否有效。这为每次调用getInstance创建一个ClientLogger,即使不是不存在。也想想它是否有意义。 == null的情况怎么会发生,如果是这样,为什么它有助于用未存储在映射中的局部变量初始化结果? – 2014-09-30 16:35:20

+0

@SimonFischer我认为你是对的,但对于空情况下,它会发生在第一次初始化时,当我试图获取记录器的实例,它是来自null,因为如果它不存在,putIfAbsent将返回null。有没有更好的方法来做到这一点? – john 2014-09-30 16:38:14

+0

你是对的,我正要纠正自己的空情况,第二个答复来了:-)但是,在这种情况下分配结果一个新的ClientLogger实例仍然没有意义,并且没有将它添加到地图中,因为在那种情况下,你正在返回与地图不同的东西。如果地图中没有以前的值,则要将新创建的实例分配给结果。但即使如此,如果存在现有的ClientLogger(),您仍然希望避免创建新的ClientLogger()。 – 2014-09-30 16:44:57

回答

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); 
    } 
} 

和拉姆达甚至不会在编译时产生的一类,尽管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; 
} 
+0

不幸的是,我现在还没有Java 8。有没有其他方法可以在Java 7中实现? – john 2014-09-30 16:51:07

+0

@ user2809564我已经更新了我的答案。如果它们很少被使用,锁并不坏。我假设您的缓存具有足够高的命中率,一旦代码变暖,它很少添加记录器。 – 2014-09-30 16:53:25

+0

是的,一旦代码是温暖的,它应该是缓存,当我尝试了你的建议,它给了我'错误'同步'?可能是那里错过的东西。 – john 2014-09-30 16:56:35

相关问题