唉,基础仍是不过硬,又在博客记录下一个小问题的解决思路。先贴出出错的代码,你们看看哪里会出问题:html
[java] view plain copyjava
- // 定义的一个map常量
- private static final Map<AlarmInfo, String> PLAN_ALARM_MAP = new LinkedHashMap<AlarmInfo, String>();
-
- // 下面是处理过程
- synchronized (PLAN_ALARM_MAP) {
- logger.infoT("PLAN_ALARM_MAP size " + PLAN_ALARM_MAP.size());
- Set<AlarmInfo> set = PLAN_ALARM_MAP.keySet();
- for (AlarmInfo alarmInfo : set) {
- if (!checkContinueAlarm(alarmInfo,
- PLAN_ALARM_MAP.get(alarmInfo))) {
- logger.infoT("clear planTask alarm!");
- // 清除告警信息
- manageAlarm(alarmInfo, mesg, true);
-
- PLAN_ALARM_MAP.remove(alarmInfo);
- // 记录下来,后面删除
- // alarmDelete.add(alarmInfo);
- }
- }
- }
看出来了吧,和List同样,在循环迭代过程当中是不能修改或者删除Map的key的,会引起java.util.concurrentmodificationexception,颇有趣的是,就像api说的,在大部分状况下会引发该异常,但有时候不会出异常,至于这个缘由没深刻研究源码,就暂且不论。发现了异常,就要知足在循环迭代Map的keySet的过程当中删除map中的符合条件key的别的解决办法,在网上查了下找到两套都能实现的办法:api
1 将要删除的key存到一个List中,在遍历完map的keySet后,再迭代这个存放删除key的List,逐一删除!安全
2 采用Iterator迭代,在迭代遇到须要删除的key时,采用迭代器的remove()方法删除,也能够避免出现异常!并发
方案1的解决代码以下:.net
[java] view plain copy线程
- private static final Map<AlarmInfo, String> PLAN_ALARM_MAP = new LinkedHashMap<AlarmInfo, String>();
-
- synchronized (PLAN_ALARM_MAP) {
-
- logger.infoT("PLAN_ALARM_MAP size " + PLAN_ALARM_MAP.size());
- Set<AlarmInfo> set = PLAN_ALARM_MAP.keySet();
- List<AlarmInfo> alarmDelete = new ArrayList<AlarmInfo>();
-
- for (AlarmInfo alarmInfo : set) {
- if (!checkContinueAlarm(alarmInfo,
- PLAN_ALARM_MAP.get(alarmInfo))) {
- logger.infoT("clear planTask alarm!");
- // 清除告警信息
- manageAlarm(alarmInfo, mesg, true);
-
-
- // 记录下来,后面删除
- alarmDelete.add(alarmInfo);
- }
- }
-
- // 进行删除
- for (AlarmInfo deleteAlarm : alarmDelete) {
- PLAN_ALARM_MAP.remove(deleteAlarm);
- }
- }
方案2代码以下:code
[java] view plain copyhtm
- private static final Map<AlarmInfo, String> PLAN_ALARM_MAP = new LinkedHashMap<AlarmInfo, String>();
-
- synchronized (PLAN_ALARM_MAP) {
-
- logger.infoT("PLAN_ALARM_MAP size " + PLAN_ALARM_MAP.size());
- Set<AlarmInfo> set = PLAN_ALARM_MAP.keySet();
-
- AlarmInfo tempAlarmInfo = null;
- for (Iterator<AlarmInfo> itr = set.iterator(); itr.hasNext();) {
-
- tempAlarmInfo = itr.next();
- if (!checkContinueAlarm(tempAlarmInfo,
- PLAN_ALARM_MAP.get(tempAlarmInfo))) {
- logger.infoT("clear planTask alarm!");
- // 清除告警信息
- manageAlarm(tempAlarmInfo, mesg, true);
- itr.remove();
- }
- }
- }
参考文献:blog
http://yulimeander.i.sohu.com/blog/view/142269299.htm
http://swincle.iteye.com/blog/746980
还有一种方法,就是使用 CopyOnWriteArrayList
由于 普通的容器他的迭代器只是一个视图,而不是一个副本;
因此这个容器的特色就是搞一个副本,因此不会报这个错误;
==========================================
在进行迭代时这个问题更改明显。Map集合共提供了三种方式来分别返回键、值、键值对的集合:
Java代码
Set<K> keySet();
Collection<V> values();
Set<Map.Entry<K,V>> entrySet();
在这三个方法的基础上,咱们通常经过以下方式访问Map的元素:
Java代码
Iterator keys = map.keySet().iterator();
while(keys.hasNext()){
map.get(keys.next());
}
在这里,有一个地方须要注意的是:获得的keySet和迭代器都是Map中元素的一个“视图”,而不是“副本”。问题也就出如今这里,当一个线程正在迭代Map中的元素时,另外一个线程可能正在修改其中的元素。此时,在迭代元素时就可能会抛出ConcurrentModificationException异常。为了解决这个问题一般有两种方法,一是直接返回元素的副本,而不是视图。这个能够经过
集合类的 toArray()方法实现,可是建立副本的方式效率比以前有所下降,特别是在元素不少的状况下;另外一种方法就是在迭代的时候锁住整个集合,这样的话效率就更低了。
参考 http://blog.csdn.net/linsongbin1/article/details/54581787
CopyOnWriteArrayList是线程安全的;
如上面的分析CopyOnWriteArrayList表达的一些思想: 一、读写分离,读和写分开 二、最终一致性 三、使用另外开辟空间的思路,来解决并发冲突