与 Guava HashBiMap 和 synchronizedBiMap 同步
Synchronization with Guava HashBiMap and synchronizedBiMap
我在多线程情况下收到 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-jre
和 Java(TM) SE Runtime Environment 1.8.0_152-b16
因为 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"
}
这与同步没有任何关系,但 BiMap
是这样工作的。您可以轻松复制它:
cache.putIfAbsent(1, URI.create("http://example.com"));
cache.putIfAbsent(2, URI.create("http://whosebug.com"));
System.out.println(cache);
// {1=http://example.com, 2=http://whosebug.com}
cache.putIfAbsent(3, URI.create("http://example.com"));
// java.lang.IllegalArgumentException: value already present: http://example.com
BiMap
is "a map that preserves the uniqueness of its values as well as that of its keys." This means that you can't put example.com again, even under different key. See also wiki page describing BiMap
:
BiMap.put(key, value)
will throw an IllegalArgumentException
if you attempt to map a key to an already-present value. If you wish to delete any preexisting entry with the specified value, use BiMap.forcePut(key, value)
instead.
在您的情况下,您可以使用 forcePut
而不会因异常而失败:
cache.forcePut(3, URI.create("http://example.com"));
System.out.println(cache);
// {2=http://whosebug.com, 3=http://example.com}
我在多线程情况下收到 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-jre
和 Java(TM) SE Runtime Environment 1.8.0_152-b16
因为 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"
}
这与同步没有任何关系,但 BiMap
是这样工作的。您可以轻松复制它:
cache.putIfAbsent(1, URI.create("http://example.com"));
cache.putIfAbsent(2, URI.create("http://whosebug.com"));
System.out.println(cache);
// {1=http://example.com, 2=http://whosebug.com}
cache.putIfAbsent(3, URI.create("http://example.com"));
// java.lang.IllegalArgumentException: value already present: http://example.com
BiMap
is "a map that preserves the uniqueness of its values as well as that of its keys." This means that you can't put example.com again, even under different key. See also wiki page describing BiMap
:
BiMap.put(key, value)
will throw anIllegalArgumentException
if you attempt to map a key to an already-present value. If you wish to delete any preexisting entry with the specified value, useBiMap.forcePut(key, value)
instead.
在您的情况下,您可以使用 forcePut
而不会因异常而失败:
cache.forcePut(3, URI.create("http://example.com"));
System.out.println(cache);
// {2=http://whosebug.com, 3=http://example.com}