(初步说明:也许这更适合代码审查?)
编辑 对自己的回答;我相信这个答案涵盖了我所有的需求/问题,但当然,欢迎发表评论。原始问题留在下面以供参考。
你好,
这里有趣的是.getSources()
方法。此方法旨在返回给定的消息源列表Locale
。
此方法的两个中心数据结构是sources
和failedLookups
,请参阅代码以获取注释。
这个特定的实现.getSources()
只能返回一个空列表或一个单元素列表,这取决于tryAndLookup()
原型的方法:
protected abstract MessageSource tryAndLookup(final Locale locale)
throws IOException;
现在,代码的逻辑如下:
- 如果该语言环境的消息源已被成功查找,则返回;
- 从此时起,没有进行任何查找;然而,这是否意味着先前的查找尝试已经完成是未知的:检查失败的查找集合,如果要查找的语言环境在那里,这是一个已知的失败,返回空列表;
- 现在,已知的情况是这个语言环境查找实际上从未执行过:执行它;根据
tryAndLookup
方法的结果,记录成功或失败。
现在,我为什么要这么费劲:我无法控制tryAndLookup()
;在返回有效源或失败之前执行可能需要过多的时间。因此,我不愿意使用粗锁或synchronized
阻塞。
/**
* Set of locales known to have failed lookup.
*
* <p>When a locale is in this set, it will not attempt to be reloaded.</p>
*/
private final Set<Locale> lookupFailures
= new CopyOnWriteArraySet<Locale>();
/**
* Set of message sources successfully looked up
*
* <p>When a source is in there, it is there permanently for now.</p>
*/
private final ConcurrentMap<Locale, MessageSource> sources
= new ConcurrentHashMap<Locale, MessageSource>();
@Override
protected final List<MessageSource> getSources(final Locale locale)
{
MessageSource source = sources.get(locale);
/*
* If found, return it
*/
if (source != null)
return Arrays.asList(source);
/*
* If it is a registered failure, return the empty list
*/
if (lookupFailures.contains(locale))
return Collections.emptyList();
/*
* OK, try and look it up. On success, register it in the sources map.
* On failure, record the failure an return the empty list.
*/
try {
source = tryAndLookup(locale);
sources.putIfAbsent(locale, source);
// EDIT: fix for bug pinpointed by JBNizet
// was:
// return Arrays.asList(source);
// now is:
return Arrays.asList(sources.get(locale));
} catch (IOException ignored) {
lookupFailures.add(locale);
return Collections.emptyList();
}
}
我的问题有三个:
- 我故意将自己限制在只对 JDK 可用的类;我选择了
ConcurrentHashMap
作为一个ConcurrentMap
实现,并且CopyOnWriteArraySet
作为一个线程安全的Set
实现;从javadoc,这些是我能找到的最好的。但我是不是在什么地方被误导了? - 我认为这段代码最终是线程安全的;例如,某些极端情况可能会导致多次查找,但这就是我这样做的原因
.putIfAbsent()
;现在我一直使用并信任 GuavaLoadingCache
用于缓存目的,这是我第一次走出这个领域;这段代码实际上是线程安全的吗? - 这段代码有一个致命缺陷:可能同时执行多个线程
tryAndLookup()
... 有什么解决方案可以让这个方法每次查找只执行一次?