java 集合并發(fā)操作出現(xiàn)的異常ConcurrentModificationException
更新時(shí)間:2009年06月04日 02:31:12 作者:
Map在遍歷時(shí)候通常 現(xiàn)獲得其鍵值的集合Set,然后用迭代器Iterator來(lái)對(duì)Map進(jìn)行遍歷。
如Java中的容器Map:
for(Person person : pList){
if(person.getGender()==Gender.MALE){
pList.remove(person); //不能在遍歷期間進(jìn)行 remove這個(gè)操作
}
}
Map在遍歷時(shí)候通常 現(xiàn)獲得其鍵值的集合Set,然后用迭代器Iterator來(lái)對(duì)Map進(jìn)行遍歷。
注意在遍歷的過(guò)程中,只能對(duì)Map中的元素進(jìn)行相應(yīng)的處理,不能把Map元素增加或者把Map元素減少,也就是說(shuō),不能改變Map的size大小,就會(huì)出現(xiàn)異常(不能在遍歷過(guò)程中修改刪除或者增加map中的元素)
報(bào)出的異常為 java.util.ConcurrentModificationException 異常
public class ConcurrentModificationExceptionextends RuntimeException
當(dāng)方法檢測(cè)到對(duì)象的并發(fā)修改,但不允許這種修改時(shí),拋出此異常。
例如,某個(gè)線程在 Collection 上進(jìn)行迭代時(shí),通常不允許另一個(gè)線性修改該 Collection。通常在這些情況下,迭代的結(jié)果是不明確的。如果檢測(cè)到這種行為,一些迭代器實(shí)現(xiàn)(包括 JRE 提供的所有通用 collection 實(shí)現(xiàn))可能選擇拋出此異常。執(zhí)行該操作的迭代器稱為快速失敗迭代器,因?yàn)榈骱芸炀屯耆?,而不?huì)冒著在將來(lái)某個(gè)時(shí)間任意發(fā)生不確定行為的風(fēng)險(xiǎn)。
注意,此異常不會(huì)始終指出對(duì)象已經(jīng)由不同 線程并發(fā)修改。如果單線程發(fā)出違反對(duì)象協(xié)定的方法調(diào)用序列,則該對(duì)象可能拋出此異常。例如,如果線程使用快速失敗迭代器在 collection 上迭代時(shí)直接修改該 collection,則迭代器將拋出此異常。
注意,迭代器的快速失敗行為無(wú)法得到保證,因?yàn)橐话銇?lái)說(shuō),不可能對(duì)是否出現(xiàn)不同步并發(fā)修改做出任何硬性保證??焖偈〔僮鲿?huì)盡最大努力拋出 ConcurrentModificationException。因此,為提高此類操作的正確性而編寫(xiě)一個(gè)依賴于此異常的程序是錯(cuò)誤的做法,正確做法是:ConcurrentModificationException 應(yīng)該僅用于檢測(cè) bug。
當(dāng)使用 fail-fast iterator 對(duì) Collection 或 Map 進(jìn)行迭代操作過(guò)程中嘗試直接修改 Collection / Map 的內(nèi)容時(shí),即使是在單線程下運(yùn)行, java.util.ConcurrentModificationException 異常也將被拋出。
Iterator 是工作在一個(gè)獨(dú)立的線程中,并且擁有一個(gè) mutex 鎖。 Iterator 被創(chuàng)建之后會(huì)建立一個(gè)指向原來(lái)對(duì)象的單鏈索引表,當(dāng)原來(lái)的對(duì)象數(shù)量發(fā)生變化時(shí),這個(gè)索引表的內(nèi)容不會(huì)同步改變,所以當(dāng)索引指針往后移動(dòng)的時(shí)候就找不到要迭代的對(duì)象,所以按照 fail-fast 原則 Iterator 會(huì)馬上拋出 java.util.ConcurrentModificationException 異常。
所以 Iterator 在工作的時(shí)候是不允許被迭代的對(duì)象被改變的。但你可以使用 Iterator 本身的方法 remove() 來(lái)刪除對(duì)象, Iterator.remove() 方法會(huì)在刪除當(dāng)前迭代對(duì)象的同時(shí)維護(hù)索引的一致性。
有意思的是如果你的 Collection / Map 對(duì)象實(shí)際只有一個(gè)元素的時(shí)候, ConcurrentModificationException 異常并不會(huì)被拋出。這也就是為什么在 javadoc 里面指出: it would be wrong to write a program that depended on this exception for its correctness: ConcurrentModificationException should be used only to detect bugs.
for(Person person : pList){
if(person.getGender()==Gender.MALE){
pList.remove(person); //不能在遍歷期間進(jìn)行 remove這個(gè)操作
}
}
Map在遍歷時(shí)候通常 現(xiàn)獲得其鍵值的集合Set,然后用迭代器Iterator來(lái)對(duì)Map進(jìn)行遍歷。
注意在遍歷的過(guò)程中,只能對(duì)Map中的元素進(jìn)行相應(yīng)的處理,不能把Map元素增加或者把Map元素減少,也就是說(shuō),不能改變Map的size大小,就會(huì)出現(xiàn)異常(不能在遍歷過(guò)程中修改刪除或者增加map中的元素)
報(bào)出的異常為 java.util.ConcurrentModificationException 異常
public class ConcurrentModificationExceptionextends RuntimeException
當(dāng)方法檢測(cè)到對(duì)象的并發(fā)修改,但不允許這種修改時(shí),拋出此異常。
例如,某個(gè)線程在 Collection 上進(jìn)行迭代時(shí),通常不允許另一個(gè)線性修改該 Collection。通常在這些情況下,迭代的結(jié)果是不明確的。如果檢測(cè)到這種行為,一些迭代器實(shí)現(xiàn)(包括 JRE 提供的所有通用 collection 實(shí)現(xiàn))可能選擇拋出此異常。執(zhí)行該操作的迭代器稱為快速失敗迭代器,因?yàn)榈骱芸炀屯耆?,而不?huì)冒著在將來(lái)某個(gè)時(shí)間任意發(fā)生不確定行為的風(fēng)險(xiǎn)。
注意,此異常不會(huì)始終指出對(duì)象已經(jīng)由不同 線程并發(fā)修改。如果單線程發(fā)出違反對(duì)象協(xié)定的方法調(diào)用序列,則該對(duì)象可能拋出此異常。例如,如果線程使用快速失敗迭代器在 collection 上迭代時(shí)直接修改該 collection,則迭代器將拋出此異常。
注意,迭代器的快速失敗行為無(wú)法得到保證,因?yàn)橐话銇?lái)說(shuō),不可能對(duì)是否出現(xiàn)不同步并發(fā)修改做出任何硬性保證??焖偈〔僮鲿?huì)盡最大努力拋出 ConcurrentModificationException。因此,為提高此類操作的正確性而編寫(xiě)一個(gè)依賴于此異常的程序是錯(cuò)誤的做法,正確做法是:ConcurrentModificationException 應(yīng)該僅用于檢測(cè) bug。
當(dāng)使用 fail-fast iterator 對(duì) Collection 或 Map 進(jìn)行迭代操作過(guò)程中嘗試直接修改 Collection / Map 的內(nèi)容時(shí),即使是在單線程下運(yùn)行, java.util.ConcurrentModificationException 異常也將被拋出。
Iterator 是工作在一個(gè)獨(dú)立的線程中,并且擁有一個(gè) mutex 鎖。 Iterator 被創(chuàng)建之后會(huì)建立一個(gè)指向原來(lái)對(duì)象的單鏈索引表,當(dāng)原來(lái)的對(duì)象數(shù)量發(fā)生變化時(shí),這個(gè)索引表的內(nèi)容不會(huì)同步改變,所以當(dāng)索引指針往后移動(dòng)的時(shí)候就找不到要迭代的對(duì)象,所以按照 fail-fast 原則 Iterator 會(huì)馬上拋出 java.util.ConcurrentModificationException 異常。
所以 Iterator 在工作的時(shí)候是不允許被迭代的對(duì)象被改變的。但你可以使用 Iterator 本身的方法 remove() 來(lái)刪除對(duì)象, Iterator.remove() 方法會(huì)在刪除當(dāng)前迭代對(duì)象的同時(shí)維護(hù)索引的一致性。
有意思的是如果你的 Collection / Map 對(duì)象實(shí)際只有一個(gè)元素的時(shí)候, ConcurrentModificationException 異常并不會(huì)被拋出。這也就是為什么在 javadoc 里面指出: it would be wrong to write a program that depended on this exception for its correctness: ConcurrentModificationException should be used only to detect bugs.
相關(guān)文章
解決Springboot不能自動(dòng)提交數(shù)據(jù)庫(kù)連接問(wèn)題
在使用SSM框架開(kāi)發(fā)時(shí),若在同一Service內(nèi)部方法間互相調(diào)用,直接使用this關(guān)鍵字會(huì)導(dǎo)致事務(wù)管理失效,從而引發(fā)如數(shù)據(jù)庫(kù)連接不足等問(wèn)題,原因是通過(guò)this調(diào)用不會(huì)經(jīng)過(guò)Spring的代理,因此不會(huì)自動(dòng)進(jìn)行事務(wù)處理2024-09-09Java將GeoHash轉(zhuǎn)化為對(duì)應(yīng)的經(jīng)緯度坐標(biāo)實(shí)例代碼
這篇文章主要介紹了Java實(shí)現(xiàn)將GeoHash轉(zhuǎn)化為對(duì)應(yīng)的經(jīng)緯度坐標(biāo)的相關(guān)資料,需要的朋友可以參考下2016-01-01SpringCloud2020.0.x版UnderTow AccessLog相關(guān)配置簡(jiǎn)介
本文詳細(xì)介紹了SpringCloud中AccessLog的相關(guān)配置,我們可以根據(jù)文中的相關(guān)數(shù)據(jù)配置出所需的AccessLog的信息以及格式,感興趣的小伙伴可以參考一下2021-08-08SpringBoot 使用 Ehcache 作為緩存的操作方法
這篇文章主要介紹了SpringBoot 如何使用 Ehcache 作為緩存,我們通過(guò)添加 Ehcache 依賴、創(chuàng)建 Ehcache 配置文件并在 Spring Boot 應(yīng)用程序的配置文件中啟用 Ehcache 緩存,來(lái)配置 Ehcache 緩存,需要的朋友可以參考下2023-06-06java?中如何實(shí)現(xiàn)?List?集合去重
這篇文章主要介紹了java?中如何實(shí)現(xiàn)?List?集合去重,List?去重指的是將?List?中的重復(fù)元素刪除掉的過(guò)程,下文操作操作過(guò)程介紹需要的小伙伴可以參考一下2022-05-05Spring?Boot監(jiān)控SQL運(yùn)行情況的全過(guò)程
這篇文章主要給大家介紹了關(guān)于Spring?Boot監(jiān)控SQL運(yùn)行情況的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用SpringBoot具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2022-02-02