与Guava HashBiMap和synchronizedBiMap同步

问题描述 投票:2回答:2

我在多线程情况下从Guava BiMap的putIfAbsent方法中获得异常。我该如何正确保护它免受线程问题的影响?

我像这样创建地图:

BiMap<Integer, java.net.URI> cache = com.google.common.collect.Maps.synchronizedBiMap(HashBiMap.create());

然后,我修改地图的唯一时间是cache.clear();cache.putIfAbsent(a,b)

我偶尔会看到这个堆栈跟踪:

java.lang.IllegalArgumentException: value already present: http://example.com
    at com.google.common.collect.HashBiMap.put(HashBiMap.java:279)
    at com.google.common.collect.HashBiMap.put(HashBiMap.java:260)
    at java.util.Map.putIfAbsent(Map.java:744)
    at com.google.common.collect.Synchronized$SynchronizedMap.putIfAbsent(Synchronized.java:1120)

这是HashBiMap或synchronizedBiMap中的错误吗?或者我是否需要为线程安全做额外的工作?

使用guava-25.0-jreJava(TM) SE Runtime Environment 1.8.0_152-b16

java guava
2个回答
3
投票

因为BiMap提供了从值到键的映射,以及从键到值的通常Map映射,所以每个值只能与单个键配对。尝试将值与多个唯一键关联将导致您看到的IllegalArgumentException

听起来您的问题与线程相关,而不是与数据相关。

举个例子,这会引发类似的异常。问题是存在值“Bar”,带有两个单独的键“Foo”和“Baz”:

    public static void main(String[] args) {
        BiMap<String, String> m = HashBiMap.create();
        m.put("Foo", "Bar");
        m.put("Baz", "Bar"); // Throws IllegalArgumentException "value already present"
    }

2
投票

这与同步没有任何关系,但它是BiMap的工作方式。您可以轻松地重现它:

cache.putIfAbsent(1, URI.create("http://example.com"));
cache.putIfAbsent(2, URI.create("http://stackoverflow.com"));
System.out.println(cache);
// {1=http://example.com, 2=http://stackoverflow.com}
cache.putIfAbsent(3, URI.create("http://example.com"));
// java.lang.IllegalArgumentException: value already present: http://example.com

BiMap是“一张保留其价值及其钥匙价值唯一性的地图。”这意味着即使在不同的密钥下,也无法再次放置example.com。另见wiki page describing BiMap

如果您尝试将键映射到已存在的值,BiMap.put(key, value)将抛出IllegalArgumentException。如果要删除任何具有指定值的预先存在的条目,请改用BiMap.forcePut(key, value)

在你的情况下你可以使用forcePut而不是失败的例外:

cache.forcePut(3, URI.create("http://example.com"));
System.out.println(cache);
// {2=http://stackoverflow.com, 3=http://example.com}
© www.soinside.com 2019 - 2024. All rights reserved.