java.util.ConcurrentModificationException
java.util.ConcurrentModificationException është një përjashtim shumë i zakonshëm kur punoni me klasat e mbledhjes Java. Klasat e Koleksionit Java janë të shpejta, që do të thotë nëse Koleksioni do të ndryshohet ndërkohë që një thread kalon mbi të duke përdorur iterator, iterator.next()
do të hedhë ConcurrentModificationException. Përjashtimi i modifikimit të njëkohshëm mund të ndodhë në rastin e një mjedisi programimi Java me shumë fije si dhe me një fije të vetme.
java.util.ConcurrentModificationException
package com.journaldev.ConcurrentModificationException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
public class ConcurrentModificationExceptionExample {
public static void main(String args[]) {
List<String> myList = new ArrayList<String>();
myList.add("1");
myList.add("2");
myList.add("3");
myList.add("4");
myList.add("5");
Iterator<String> it = myList.iterator();
while (it.hasNext()) {
String value = it.next();
System.out.println("List Value:" + value);
if (value.equals("3"))
myList.remove(value);
}
Map<String, String> myMap = new HashMap<String, String>();
myMap.put("1", "1");
myMap.put("2", "2");
myMap.put("3", "3");
Iterator<String> it1 = myMap.keySet().iterator();
while (it1.hasNext()) {
String key = it1.next();
System.out.println("Map Value:" + myMap.get(key));
if (key.equals("2")) {
myMap.put("1", "4");
// myMap.put("4", "4");
}
}
}
}
Programi i mësipërm do të hedhë java.util.ConcurrentModificationException
kur të ekzekutohet, siç tregohet më poshtë në regjistrat e konsolës.
List Value:1
List Value:2
List Value:3
Exception in thread "main" java.util.ConcurrentModificationException
at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:937)
at java.base/java.util.ArrayList$Itr.next(ArrayList.java:891)
at com.journaldev.ConcurrentModificationException.ConcurrentModificationExceptionExample.main(ConcurrentModificationExceptionExample.java:22)
Nga gjurmimi i stivës së daljes, është e qartë se përjashtimi i modifikimit të njëkohshëm hidhet kur ne thërrasim funksionin iterator next()
. Nëse po pyesni se si Iterator kontrollon modifikimin, zbatimi i tij është i pranishëm në klasën AbstractList, ku përcaktohet një variabël int modCount. ModCount siguron numrin e herëve që madhësia e listës është ndryshuar. Vlera modCount përdoret në çdo thirrje tjetër() për të kontrolluar ndonjë modifikim në një funksion checkForComodification()
. Tani, komentoni pjesën e listës dhe ekzekutoni përsëri programin. Do të shihni që nuk po hidhet asnjë Përjashtim i Modifikimit Njëkohshëm. Prodhimi:
Map Value:3
Map Value:2
Map Value:4
Meqenëse po përditësojmë vlerën kryesore ekzistuese në myMap, madhësia e tij nuk është ndryshuar dhe nuk po marrim ConcurrentModificationException. Dalja mund të jetë e ndryshme në sistemin tuaj sepse grupi i tasteve HashMap nuk është i renditur si një listë. Nëse e çkomentoni deklaratën ku po shtoj një vlerë të re çelësi në HashMap, kjo do të shkaktojë ConcurrentModificationException.
Për të shmangur Përjashtimin e Modifikimit të Njëkohshëm në një mjedis me shumë fije
- Ju mund ta konvertoni listën në një grup dhe më pas të përsërisni në grup. Kjo qasje funksionon mirë për listën me madhësi të vogël ose të mesme, por nëse lista është e madhe, atëherë do të ndikojë shumë në performancën.
- Mund ta kyçni listën gjatë përsëritjes duke e vendosur në një bllok të sinkronizuar. Kjo qasje nuk rekomandohet sepse do të ndërpresë përfitimet e multithreading.
- Nëse jeni duke përdorur JDK1.5 ose më të lartë, atëherë mund të përdorni klasat ConcurrentHashMap dhe CopyOnWriteArrayList. Kjo është qasja e rekomanduar për të shmangur përjashtimin e modifikimit të njëkohshëm.
Për të shmangur Përjashtimin e Modifikimit të Njëkohshëm në një mjedis me një filetim të vetëm
Ju mund të përdorni funksionin iterator remove()
për të hequr objektin nga objekti themelor i koleksionit. Por në këtë rast, ju mund të hiqni të njëjtin objekt dhe jo ndonjë objekt tjetër nga lista. Le të ekzekutojmë një shembull duke përdorur klasat e Koleksionit të njëkohshëm.
package com.journaldev.ConcurrentModificationException;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
public class AvoidConcurrentModificationException {
public static void main(String[] args) {
List<String> myList = new CopyOnWriteArrayList<String>();
myList.add("1");
myList.add("2");
myList.add("3");
myList.add("4");
myList.add("5");
Iterator<String> it = myList.iterator();
while (it.hasNext()) {
String value = it.next();
System.out.println("List Value:" + value);
if (value.equals("3")) {
myList.remove("4");
myList.add("6");
myList.add("7");
}
}
System.out.println("List Size:" + myList.size());
Map<String, String> myMap = new ConcurrentHashMap<String, String>();
myMap.put("1", "1");
myMap.put("2", "2");
myMap.put("3", "3");
Iterator<String> it1 = myMap.keySet().iterator();
while (it1.hasNext()) {
String key = it1.next();
System.out.println("Map Value:" + myMap.get(key));
if (key.equals("1")) {
myMap.remove("3");
myMap.put("4", "4");
myMap.put("5", "5");
}
}
System.out.println("Map Size:" + myMap.size());
}
}
Prodhimi i programit të mësipërm është paraqitur më poshtë. Ju mund të shihni se nuk ka asnjë Përjashtim të Modifikimit Concurrent që hidhet nga programi.
List Value:1
List Value:2
List Value:3
List Value:4
List Value:5
List Size:6
Map Value:1
Map Value:2
Map Value:4
Map Value:5
Map Size:4
Nga shembulli i mësipërm është e qartë se:
-
Concurrent Collection classes can be modified safely, they will not throw ConcurrentModificationException.
-
In case of CopyOnWriteArrayList, iterator doesn’t accommodate the changes in the list and works on the original list.
-
In case of ConcurrentHashMap, the behaviour is not always the same.For condition:
if(key.equals("1")){ myMap.remove("3");}
Output is:
Map Value:1 Map Value:null Map Value:4 Map Value:2 Map Size:4
It is taking the new object added with key “4” but not the next added object with key “5”. Now if I change the condition to below.
if(key.equals("3")){ myMap.remove("2");}
Output is:
Map Value:1 Map Value:3 Map Value:null Map Size:4
In this case, it’s not considering the newly added objects. So if you are using ConcurrentHashMap then avoid adding new objects as it can be processed depending on the keyset. Note that the same program can print different values in your system because HashMap keyset is not ordered.
Përdorni për lak për të shmangur java.util.ConcurrentModificationException
Nëse jeni duke punuar në një mjedis me një fillesë dhe dëshironi që kodi juaj të kujdeset për objektet shtesë të shtuara në listë, atëherë mund ta bëni këtë duke përdorur for loop dhe jo një Iterator.
for(int i = 0; i<myList.size(); i++){
System.out.println(myList.get(i));
if(myList.get(i).equals("3")){
myList.remove(i);
i--;
myList.add("6");
}
}
Vini re se po e zvogëloj numëruesin sepse po heq të njëjtin objekt, nëse duhet të hiqni objektin tjetër ose më larg, atëherë nuk keni nevojë të zvogëloni numëruesin. Provojeni vetë. :) Një gjë më shumë: Ju do të merrni ConcurrentModificationException nëse do të përpiqeni të modifikoni strukturën e listës origjinale me nënList. Le ta shohim këtë me një shembull të thjeshtë.
package com.journaldev.ConcurrentModificationException;
import java.util.ArrayList;
import java.util.List;
public class ConcurrentModificationExceptionWithArrayListSubList {
public static void main(String[] args) {
List<String> names = new ArrayList<>();
names.add("Java");
names.add("PHP");
names.add("SQL");
names.add("Angular 2");
List<String> first2Names = names.subList(0, 2);
System.out.println(names + " , " + first2Names);
names.set(1, "JavaScript");
// check the output below. :)
System.out.println(names + " , " + first2Names);
// Let's modify the list size and get ConcurrentModificationException
names.add("NodeJS");
System.out.println(names + " , " + first2Names); // this line throws exception
}
}
Rezultati i programit të mësipërm është:
[Java, PHP, SQL, Angular 2] , [Java, PHP]
[Java, JavaScript, SQL, Angular 2] , [Java, JavaScript]
Exception in thread "main" java.util.ConcurrentModificationException
at java.base/java.util.ArrayList$SubList.checkForComodification(ArrayList.java:1282)
at java.base/java.util.ArrayList$SubList.listIterator(ArrayList.java:1151)
at java.base/java.util.AbstractList.listIterator(AbstractList.java:311)
at java.base/java.util.ArrayList$SubList.iterator(ArrayList.java:1147)
at java.base/java.util.AbstractCollection.toString(AbstractCollection.java:465)
at java.base/java.lang.String.valueOf(String.java:2801)
at java.base/java.lang.StringBuilder.append(StringBuilder.java:135)
at com.journaldev.ConcurrentModificationException.ConcurrentModificationExceptionWithArrayListSubList.main(ConcurrentModificationExceptionWithArrayListSubList.java:26)
Sipas dokumentacionit të ArrayList SubList, modifikimet strukturore lejohen vetëm në listën e kthyer me metodën e subList. Të gjitha metodat në listën e kthyer kontrolloni fillimisht për të parë nëse modCount aktual i listës mbështetëse është i barabartë me vlerën e tij të pritur dhe hidhni një Përjashtim Modifikimi Concurrent nëse nuk është.
Ju mund të shkarkoni të gjithë kodin shembull nga depoja jonë e GitHub.