java - Java 中的按键阻塞映射

标签 java concurrency java.util.concurrent

我正在处理一些涉及创建昂贵对象并将它们缓存在 Map 中的第三方库代码。现有的实现类似于

lock.lock()
try {
    Foo result = cache.get(key);
    if (result == null) {
        result = createFooExpensively(key);
        cache.put(key, result);
    }
    return result;
} finally {
    lock.unlock();
}

显然,当可以独立创建用于不同Foos 时,这显然不是最佳设计。

我目前的技巧是使用 FuturesMap:

lock.lock();
Future<Foo> future;
try {
    future = allFutures.get(key);
    if (future == null) {
        future = executorService.submit(new Callable<Foo>() {
            public Foo call() {
                return createFooExpensively(key);
            }
        });
        allFutures.put(key, future);
    }
} finally {
    lock.unlock();
}

try {
    return future.get();
} catch (InterruptedException e) {
    throw new MyRuntimeException(e);
} catch (ExecutionException e) {
    throw new MyRuntimeException(e);
}

但这似乎...有点老套,原因有二:

  1. 这项工作是在任意池化线程上完成的。我会很高兴有这份工作 在尝试获取该特定 key 的第一个线程上完成,特别是因为 无论如何它都会被阻止。
  2. 即使 Map 已完全填充,我们仍会通过 Future.get() 获取 结果。我希望这很便宜,但它很难看。

我想要的是将 cache 替换为 Map ,它将阻止获取给定键的 直到该键具有值,但同时允许其他获取。有这样的东西吗?或者有人有更简洁的方法来替代 FuturesMap 吗?

最佳答案

为每个键创建一个锁听起来很诱人,但这可能不是您想要的,尤其是当键的数量很大时。

由于您可能需要为每个键创建专用(读写)锁,因此它会影响您的内存使用。此外,如果并发性确实很高,那么在给定有限数量的内核的情况下,这种细粒度可能会达到 yield 递减点。

在这种情况下,ConcurrentHashMap 通常是一个足够好的解决方案。它通常提供完全读取器并发(通常读取器不会阻塞),并且更新可以并发到所需的并发级别。这为您提供了很好的可扩展性。上面的代码可以用 ConcurrentHashMap 表示如下:

ConcurrentMap<Key,Foo> cache = new ConcurrentHashMap<>();
...
Foo result = cache.get(key);
if (result == null) {
  result = createFooExpensively(key);
  Foo old = cache.putIfAbsent(key, result);
  if (old != null) {
    result = old;
  }
}

直接使用 ConcurrentHashMap 确实有一个缺点,就是多个线程可能会发现 key 没有被缓存,并且每个线程都可能调用 createFooExpensively()。因此,一些线程可能会做一些一次性的工作。为避免这种情况,您需要使用“Java 并发实践”中提到的内存器模式。

不过话又说回来,Google 的好心人已经以 CacheBuilder 的形式为您解决了这些问题。 :

LoadingCache<Key,Foo> cache = CacheBuilder.newBuilder().
  concurrencyLevel(32).
  build(new CacheLoader<Key,Foo>() {
    public Foo load(Key key) {
      return createFooExpensively(key);
    }
  });

...
Foo result = cache.get(key);

关于java - Java 中的按键阻塞映射,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16821767/

相关文章:

java - ExecutorService awaitTermination 卡住了

c++ - 如何同步访问 NAS 上文件的两个进程?

java - 不能为 ForkJoinPool 提供线程工厂或名称模式吗?

java - 不可能(?): NullPointerException on ConcurrentLinkedQueue. 大小()

java - 如何向表添加监听器?

python - 在 Python 中使用 Multiprocessing+Threads 加速应用程序时遇到问题

scala - ConcurrentHashMap[String, AtomicInteger] 或 ConcurrentHashMap[String, Int] 用于线程安全计数器?

java - 我在使用 VR android.view.InflateException : Binary XML file line #44: Error inflating class com. google.vr.sdk.widgets.video.VrVideoView 时出错

java - 在 java 中将 List<Rows> 转换为 List<long>

java - Jersey Rest - JsonMappingException : No suitable constructor found for type [simple type, 类]