唉,基礎仍是不過硬,又在博客記錄下一個小問題的解決思路。先貼出出錯的代碼,你們看看哪裏會出問題: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表達的一些思想: 一、讀寫分離,讀和寫分開 二、最終一致性 三、使用另外開闢空間的思路,來解決併發衝突