最近在写一个仿真程序,涉及到一个A-->B的映射,自然想到了用HashMap或者HashCode
我用了类似HashMap<Integer, Ctime> h1 = new HashMap<Integer, Ctime>();的全局名称列表
但是,由于是多线程环境下的使用,涉及到了两个问题,
第一,由于Hashtable 中的方法是同步的,而HashMap中的方法在缺省情况下是非同步的。即是说,在多线程应用程序中,不用专门的操作就安全地可以使用Hashtable 了;而对于HashMap,则需要额外的同步机制。但HashMap的同步问题可通过Collections的一个静态方法得到解决:Map Collections.synchronizedMap(Map m)
那么这个机制到底提供了多少保证呢?
第二,我在实现中,需要频繁地对某些HashMap进行遍历,利用iterator,如果出现了并发访问,上面的静态方法变化后的map是提供了什么样粒度的锁的保护? Iterator<ITWMessage<?, ?>> sMsgIt = sentMessages.iterator();
while (sMsgIt.hasNext()) {
ITWMessage<?, ?> sMsg = sMsgIt.next(); if (sMsg.getTimeStamp().compareTo(msg.getTimeStamp()) > 0
|| (simultaniousAntiMessagSending(msg, sMsg) == true && sMsg
.getTimeStamp().compareTo(msg.getTimeStamp()) == 0)) { // convert message into anti message
sMsg.setAntiMessage(true); ITimeWarpLogicalProcessSimulator receiver = (ITimeWarpLogicalProcessSimulator) sMsg
.getTo(); // send the anti message
receiver.receiveMessage(sMsg); // get rid of the message shadow
sMsgIt.remove();
}
我是否还需要显示地进行synchronized(){}以及pv操作对整个遍历过程进行保护呢,
另外,快速失败有多大影响呢?
谢谢!
我用了类似HashMap<Integer, Ctime> h1 = new HashMap<Integer, Ctime>();的全局名称列表
但是,由于是多线程环境下的使用,涉及到了两个问题,
第一,由于Hashtable 中的方法是同步的,而HashMap中的方法在缺省情况下是非同步的。即是说,在多线程应用程序中,不用专门的操作就安全地可以使用Hashtable 了;而对于HashMap,则需要额外的同步机制。但HashMap的同步问题可通过Collections的一个静态方法得到解决:Map Collections.synchronizedMap(Map m)
那么这个机制到底提供了多少保证呢?
第二,我在实现中,需要频繁地对某些HashMap进行遍历,利用iterator,如果出现了并发访问,上面的静态方法变化后的map是提供了什么样粒度的锁的保护? Iterator<ITWMessage<?, ?>> sMsgIt = sentMessages.iterator();
while (sMsgIt.hasNext()) {
ITWMessage<?, ?> sMsg = sMsgIt.next(); if (sMsg.getTimeStamp().compareTo(msg.getTimeStamp()) > 0
|| (simultaniousAntiMessagSending(msg, sMsg) == true && sMsg
.getTimeStamp().compareTo(msg.getTimeStamp()) == 0)) { // convert message into anti message
sMsg.setAntiMessage(true); ITimeWarpLogicalProcessSimulator receiver = (ITimeWarpLogicalProcessSimulator) sMsg
.getTo(); // send the anti message
receiver.receiveMessage(sMsg); // get rid of the message shadow
sMsgIt.remove();
}
我是否还需要显示地进行synchronized(){}以及pv操作对整个遍历过程进行保护呢,
另外,快速失败有多大影响呢?
谢谢!
顺便问哈,lz说的“map是提供了什么样粒度的锁的保护”是啥意思?
那么这个机制到底提供了多少保证呢?我记得这个类就是个封装,相当于“罩着”不同步的hashmap,它自己的方法是同步的,但是在同步的方法里面,它回去调用非同步的hashmap的相关方法完成操作。这样做的好处就是对同步操作没有一棒子打死,比如,你用hashtable,即使你知道这个操作不会有同步问题,也是同步了的,但是如果使用这个封装,那就可以在确定没有同步问题的情况下直接绕过这个封装直接使用hashmap。如果没有这种需求,其实封装后的hashmap和直接使用hashtable是没本质区别的。第二,我在实现中,需要频繁地对某些HashMap进行遍历,利用iterator,如果出现了并发访问,上面的静态方法变化后的map是提供了什么样粒度的锁的保护?首先,一个比较通用的用例是在每次遍历之前保存一个浅拷贝(印象中hashtable和hashmap都有一个叫做copy的方法,会返回一个浅拷贝),相当于在你循环之前,先把当时hashmap中的元素搞个快照,然后用这个快照或者浅拷贝用来遍历,不去考虑遍历过程中新增加的元素,对于已经删除的元素,看你具体的需求了,如果要求不处理,那可以在删除一个元素的标记一下这个元素,然后在遍历的时候检查一下这个标记,如果是没有删除的再处理。
该方法只保证返回的对象,其Map接口下的方法是线程安全的。
2.当使用Collections.synchronizedMap(Map m)返回的对象,进行遍历时,其Iterator对象的方法,并不是线程安全的。
Collections.synchronizedMap(Map m)封装的返回对象,其同步锁对象,就是返回对象本身,
也就是说,当你遍历Collections.synchronizedMap(Map m)返回的对象时,可以采用如下方法:Map obj = Collections.synchronizedMap(Map m);
synchronized(obj){
for(Iterator itr = boj.keySet().iterator();itr.hasNext();){
//遍历的代码
......
}
}
It is imperative that the user manually synchronize on the returned map when iterating over any of its collection views: Map m = Collections.synchronizedMap(new HashMap());
...
Set s = m.keySet(); // Needn't be in synchronized block
...
synchronized(m) { // Synchronizing on m, not s!
Iterator i = s.iterator(); // Must be in synchronized block
while (i.hasNext())
foo(i.next());
}
Failure to follow this advice may result in non-deterministic behavior.
The returned map will be serializable if the specified map is serializable.
public class ConcurrentHashMap<K,V>extends AbstractMap<K,V>implements ConcurrentMap<K,V>, Serializable支持获取的完全并发和更新的所期望可调整并发的哈希表。此类遵守与 Hashtable 相同的功能规范,并且包括对应于 Hashtable 的每个方法的方法版本。不过,尽管所有操作都是线程安全的,但获取操作不 必锁定,并且不 支持以某种防止所有访问的方式锁定整个表。此类可以通过程序完全与 Hashtable 进行互操作,这取决于其线程安全,而与其同步细节无关。 获取操作(包括 get)通常不会受阻塞,因此,可能与更新操作交迭(包括 put 和 remove)。获取会影响最近完成的 更新操作的结果。对于一些聚合操作,比如 putAll 和 clear,并发获取可能只影响某些条目的插入和移除。类似地,在创建迭代器/枚举时或自此之后,Iterators 和 Enumerations 返回在某一时间点上影响哈希表状态的元素。它们不会 抛出 ConcurrentModificationException。不过,迭代器被设计成每次仅由一个线程使用。 这允许通过可选的 concurrencyLevel 构造方法参数(默认值为 16)来引导更新操作之间的并发,该参数用作内部调整大小的一个提示。表是在内部进行分区的,试图允许指示无争用并发更新的数量。因为哈希表中的位置基本上是随意的,所以实际的并发将各不相同。理想情况下,应该选择一个尽可能多地容纳并发修改该表的线程的值。使用一个比所需要的值高很多的值可能会浪费空间和时间,而使用一个显然低很多的值可能导致线程争用。对数量级估计过高或估计过低通常都会带来非常显著的影响。当仅有一个线程将执行修改操作,而其他所有线程都只是执行读取操作时,才认为某个值是合适的。此外,重新调整此类或其他任何种类哈希表的大小都是一个相对较慢的操作,因此,在可能的时候,提供构造方法中期望表大小的估计值是一个好主意。 此类及其视图和迭代器实现了 Map 和 Iterator 接口的所有可选 方法。 此类与 Hashtable 相似,但与 HashMap 不同,它不 允许将 null 用作键或值。 此类是 Java Collections Framework 的成员。