HashMap缓存中的同步
我有一个人们要求资源的网络应用程序。为了提高效率,使用同步哈希映射缓存此资源。这里的问题是当同时为同一个未缓存的资源发出两个不同的请求时:检索资源的操作会占用大量内存,所以我想避免为同一个资源多次调用它。
有人可以告诉我,以下代码段是否存在任何潜在问题?提前致谢。
private Map<String, Resource> resources = Collections.synchronizedMap(new HashMap<String, Resource>());
public void request(String name) {
Resource resource = resources.get(name);
if (resource == null) {
synchronized(this) {
if (resources.get(name) == null) {
resource = veryCostlyOperation(name); // This should only be invoked once per resource...
resources.put(resource);
} else {
resource = resources.get(name);
}
}
}
...
}
没有找到相关结果
已邀请:
4 个回复
部窖空
块内执行
来创建不必要的争用,因此许多线程无法同时检索其(独立)资源。这可以通过使用
作为地图的值来解决:
眠缝
。如果同一类中的任何其他代码也同步到
,则这些块中只有一个会立即运行。也许没有其他事情可以做到这一点,那很好。不过,我总是担心下一个程序员要做什么。 (或者在我忘记这段代码的三个月内我自己) 我建议创建一个通用的同步对象,然后同步它。 private final Object resourceCreationSynchObject = new Object(); 然后 synchronized(this.resourceCreationSynchObject){ ... } 否则,这完全符合您的要求。它确保不能并行调用
。 此外,在
区块内第二次重新获取资源是一个很好的想法。这是必要的,并且第一次调用外部确保在资源可用时不进行同步。但是没有理由第三次称呼它。首先在
块内,将
再次设置为
,然后将该变量检查为null。这将阻止你在
条款中再次调用
。
盟犯涩沟都
而不是同步
将允许多次调用get方法而不锁定。 可能不需要在
而不是
上进行同步,但这取决于代码的其余部分。
净爽
映射中的get()由映射同步,但检查结果为null不受任何保护。如果多个线程输入此请求相同的“名称”,则所有线程都将看到来自resources.get()的null结果,直到实际完成1,000Operation并将资源放入资源映射。 一种更简单,更有效但可扩展性更低的方法是使用法线贴图并使整个请求方法同步。除非它在实践中实际上是一个问题,否则我会选择简单的方法。 为了获得更高的可伸缩性,您可以通过在synchronized(this)之后再次检查映射来修复代码,以捕获上面概述的情况。它仍然不会提供最佳的可伸缩性,因为synchronized(this)只允许一个线程执行昂贵的操作,而在许多实际情况下,您只希望阻止同一资源的多次执行,同时允许对不同资源的并发请求。在这种情况下,您需要一些工具来同步所请求的资源。一个非常基本的例子:
这只是一个粗略的草图。基本上,它为ResourceEntry进行同步查找,然后在ResourceEntry上进行同步,以确保特定资源仅构建一次。