今天我在采访中提出了一个问题。 问题是Collections.synchronizedMap()是 用于同步地图,默认情况下不像hashmap那样是线程安全的。 他的问题是,我们可以在这个方法中传递任何类型的地图。 因此,当我们在此方法中传递哈希表时,效果是什么,因为哈希表默认是同步的。
答案 0 :(得分:3)
地图的行为将是相同的,但性能会受到影响,因为每个方法将获得两个同步锁而不是一个。
例如,考虑在结果地图上调用方法size()
。 Collections.SynchronizedMap
类中的实现如下所示:
public int size() {
synchronized(mutex) {return m.size();} // first lock
}
...其中,m.size()
调用Hashtable
中的实现:
public synchronized int size() { // second lock
return count;
}
第一个锁定对象是mutex
中的SynchronizedMap
字段。第二个锁是隐式的 - Hashtable
实例本身。
答案 1 :(得分:2)
您将有两个同步级别:一个在同步映射本身级别,由互斥对象实现,另一个在包装实例级别:
public boolean isEmpty() {
// first level synchronization
synchronized(mutex) {
// second level synchronization if c is a Hashtable
return c.isEmpty();
}
}
不需要额外的同步,可能导致性能下降。
另一个影响是,您将无法使用Hashtable
之类的Hashtable#elements
来使用API,因为包装的集合现在严格来说是Map
个实例。
答案 2 :(得分:1)
它将从SynchronizedMap
java.util.Collections
public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m) {
return new SynchronizedMap<>(m);
}
synchronizedMap()
方法无法区分传递给它的Map
类型。
答案 3 :(得分:0)
“他的问题是,但我们可以在这种方法中传递任何类型的地图。”
答案是肯定的,因为SynchronizedMap
的构造函数接受其签名中的每个Map
。
“那么当我们在此方法中传递哈希表时会产生什么影响,因为哈希表默认是同步的”
答案是:我们对ConcurrentHashMap
表示无知,这很可能是使用的工具,而不是阻塞实现。
答案 4 :(得分:0)
如果您在SynchronizedCollection
中看到代码。这些方法会将调用委托给底层集合,但会在调用之上添加synchronized块,如此
public int size() {
synchronized (mutex) {return c.size();}
}
HashTable
类
public synchronized int size() {
return count;
}
因此,如果您将HashTable
传递给SynchronizedCollection
,则访问SynchronizedCollection
的线程必须为同步块一次锁定2级,而对于同步方法则需要另一级锁。
如果有其他线程直接使用HashTable对象,即使线程在SynchronizedCollection
上获得锁定,它们也可以使用SynchronizedCollection
阻塞线程。