在多线程情况下,我从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还是syncedBiMap中的错误吗?还是我需要做额外的工作以确保线程安全?
使用guava-25.0-jre
和Java(TM) SE Runtime Environment 1.8.0_152-b16
答案 0 :(得分:3)
因为BiMap
提供了从值到键的映射,以及通常的Map
从键到值的映射,所以每个值只能与单个键配对。尝试将一个值与多个唯一键相关联会导致您看到IllegalArgumentException
。
听起来您的问题与线程无关,而与数据有关。
例如,这将引发类似的异常。问题是存在带有两个单独的键“ Foo”和“ Baz”的值“ Bar”:
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"
}
答案 1 :(得分: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}