Fail-fast vs. fail-safe-iteratorer
Forstå ConcurrentModificationException og fail-fast-adfærd, og hvornår De skal bruge snapshot-iteratorer.
Fail-fast vs. fail-safe-iteratorer er en gratis Java Academy-lektion på CoddyKit. Dette er lektion 4 af 4. Du kan læse hele lektionen gratis nedenfor — og derefter øve dig praktisk i browseren med en indbygget kodeeditor og en AI-vejleder, der er tilgængelig døgnet rundt. Den er en del af læringsforløbet i Java Academy, og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. Java Academy-kurset indeholder 4 lektioner i alt.
Fail-fast- og fail-safe-iteratorer
Java-iteratorer kategoriseres efter deres adfærd, når den underliggende samling ændres under iteration: fail-fast-iteratorer kaster straks en undtagelse, mens fail-safe-iteratorer fortsætter på et øjebliksbillede.
Fail-fast-iteratorer
De fleste almindelige Java-samlinger (ArrayList, HashMap og TreeSet) bruger fail-fast-iteratorer. De holder styr på en modCount — en tæller for ændringer. Enhver strukturel ændring under iteration udløser ConcurrentModificationException.
import java.util.*;
List<String> list = new ArrayList<>(List.of("a", "b", "c"));
try {
for (String s : list) {
list.add("x"); // structural modification — throws!
}
} catch (ConcurrentModificationException e) {
System.out.println("ConcurrentModificationException caught!");
}
// Same for HashMap, TreeMap, HashSet, etc.Hvorfor fail-fast findes
Fail-fast-adfærd er en hjælp til fejlfinding — den afslører fejl med det samme i stedet for at lade iterationen fortsætte med en ødelagt tilstand. Alternativet (ubemærket ødelæggelse af data) er langt sværere at fejlfinde.
// modCount is incremented on every structural change
// (add, remove, clear on ArrayList)
// Iterator records modCount on creation
// On each next(), Iterator checks: if current modCount != expected, throw CME
// This detects bugs like:
List<Integer> nums = new ArrayList<>(List.of(1,2,3,4,5));
for (Integer n : nums) {
if (n == 3) nums.remove(n); // bug caught immediately
}
// ConcurrentModificationException — not silent wrong resultsSikker fjernelse: Iterator.remove()
Den eneste sikre ændring under en Iterator-baseret iteration er Iterator.remove() — den fjerner det senest returnerede element og opdaterer modCount.
List<Integer> nums = new ArrayList<>(List.of(1,2,3,4,5,6));
Iterator<Integer> it = nums.iterator();
while (it.hasNext()) {
int n = it.next();
if (n % 2 == 0) it.remove(); // safe: updates modCount
}
System.out.println(nums); // [1, 3, 5]removeIf: Det moderne alternativ
Java 8 tilføjede removeIf(Predicate) til Collection — en renere måde at fjerne elementer, der opfylder en betingelse, uden manuel håndtering af iteratoren.
List<String> names = new ArrayList<>(List.of("Alice", "Bob", "Ann", "Charlie"));
// Modern: removeIf handles iteration internally
names.removeIf(name -> name.startsWith("A"));
System.out.println(names); // [Bob, Charlie]
// Equivalent but verbose iterator approach:
Iterator<String> it = names.iterator();
while (it.hasNext()) {
if (it.next().startsWith("A")) it.remove();
}Fail-safe-iteratorer: CopyOnWriteArrayList
CopyOnWriteArrayList bruger en fail-safe-iterator. Den gennemløber et øjebliksbillede, der blev taget, da iteratoren blev oprettet — ændringer påvirker ikke det igangværende gennemløb.
import java.util.concurrent.CopyOnWriteArrayList;
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
list.addAll(List.of("a", "b", "c"));
for (String s : list) {
list.add("x"); // no ConcurrentModificationException!
System.out.print(s + " "); // prints a, b, c (snapshot)
}
System.out.println();
System.out.println(list); // [a, b, c, x, x, x] — modified copyConcurrentHashMap-iterator
ConcurrentHashMap bruger svagt konsistente iteratorer — de afspejler tilstanden på et tidspunkt under eller efter oprettelsen af iteratoren. Der kastes ingen CME, og iteratoren ser måske de samtidige opdateringer, måske ikke.
import java.util.concurrent.ConcurrentHashMap;
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
map.put("a", 1); map.put("b", 2); map.put("c", 3);
for (Map.Entry<String, Integer> e : map.entrySet()) {
map.put("d", 4); // no CME — weakly consistent
System.out.println(e.getKey() + "=" + e.getValue());
}
// May or may not print "d=4" — weakly consistentCollections.synchronizedList
Collections.synchronizedList er IKKE fail-safe — den bruger stadig fail-fast-iteratorer, men blokerer samtidig adgang. Du skal selv synkronisere under gennemløbet.
import java.util.*;
List<String> syncList = Collections.synchronizedList(new ArrayList<>());
syncList.addAll(List.of("a", "b", "c"));
// Must synchronize during iteration!
synchronized (syncList) {
for (String s : syncList) {
System.out.println(s);
}
}
// Without the synchronized block, CME is still possible from another threadMønsteret med en øjebliksbillede-iterator
Opret selv et øjebliksbillede, så du sikkert kan gennemløbe en ændrbar samling uden synkronisering.
import java.util.*;
List<String> original = new ArrayList<>(List.of("a", "b", "c"));
// Take a snapshot copy before iterating
List<String> snapshot = List.copyOf(original);
for (String s : snapshot) {
// Safe to modify original during snapshot iteration
original.remove(s);
System.out.println("Removed: " + s);
}
System.out.println(original); // []Afvejninger ved CopyOnWrite
CopyOnWriteArrayList er fail-safe, men dyr ved skriveoperationer. Hver ændring opretter en fuld kopi.
- Godt til: mange læsninger og få skrivninger (lister med hændelseslyttere)
- Dårligt til: hyppige ændringer og store samlinger
Opsamling: Fail-fast og fail-safe
Opsummering af sammenligningen:
- Fail-fast: ArrayList, HashMap og TreeMap — kaster CME ved ændringer og kontrollerer modCount
- Fail-safe/svagt konsistent: ConcurrentHashMap og CopyOnWriteArrayList — ingen CME, gennemløber et øjebliksbillede eller samtidige data
- Sikre ændringsmetoder: Iterator.remove(), removeIf() og replaceAll()
Praktisk: Oprydning efter udløbne sessioner
Fjern udløbne sessioner sikkert fra en samtidig liste ved hjælp af passende mønstre.
import java.util.*;
import java.util.concurrent.*;
class SessionManager {
private final CopyOnWriteArrayList<Session> sessions = new CopyOnWriteArrayList<>();
record Session(String id, long expiresAt) {
boolean isExpired() { return System.currentTimeMillis() > expiresAt; }
}
void add(Session s) { sessions.add(s); }
// Safe to call from multiple threads while iterating
void purgeExpired() {
sessions.removeIf(Session::isExpired);
}
List<Session> active() {
return sessions.stream().filter(s -> !s.isExpired()).toList();
}
}Hurtigt tjek
Hvilken undtagelse kastes, hvis du ændrer en ArrayList under gennemløbet?
Opsamling: Fail-fast- og fail-safe-iteratorer
Vigtigste pointer:
- Fail-fast-iteratorer (ArrayList og HashMap) kaster ConcurrentModificationException ved samtidige ændringer
- Fail-safe-iteratorer (CopyOnWriteArrayList og ConcurrentHashMap) tillader ændringer uden at kaste en undtagelse
- Iterator.remove() er den eneste sikre måde at fjerne elementer under et fail-fast-gennemløb
- removeIf() er det moderne og renere alternativ til manuel Iterator.remove()
- CopyOnWriteArrayList: sikker i scenarier med mange læsninger og få skrivninger
- ConcurrentHashMap: svagt konsistent — ingen CME, og samtidige ændringer ses måske, måske ikke
Lær Java med en AI-underviser — gratis
Skriv og kør rigtig kode i din browser, få øjeblikkelig hjælp fra en AI-underviser døgnet rundt, og fortsæt, hvor du slap, på web eller i appen.
- Kurser
- 104
- Lektioner
- 374
Ofte stillede spørgsmål
Er lektionen “Fail-fast vs. fail-safe-iteratorer” gratis?
Ja — hele teksten til “Fail-fast vs. fail-safe-iteratorer” kan læses gratis her på nettet. Hvis du vil øve dig interaktivt med en indbygget kodeeditor og en AI-vejleder døgnet rundt og få adgang til resten af Java Academy-kurset, skal du opgradere til CoddyKit PRO. Java Academy-kurset indeholder 4 lektioner i alt.
Hvad lærer jeg i “Fail-fast vs. fail-safe-iteratorer”?
Forstå ConcurrentModificationException og fail-fast-adfærd, og hvornår De skal bruge snapshot-iteratorer. Du øver dig i Java Academy med praktisk kode, som du kører direkte i browseren, og en AI-vejleder døgnet rundt besvarer dine spørgsmål, mens du arbejder dig gennem lektionen.
Skal jeg have erfaring for at begynde på Java Academy?
Der kræves ingen tidligere erfaring. Java Academy på CoddyKit er tilrettelagt for både begyndere og øvede, så du kan starte her eller fra begyndelsen og lære i dit eget tempo. Dette er lektion 4 af 4.
Hvor lang tid tager lektionen “Fail-fast vs. fail-safe-iteratorer”?
De fleste CoddyKit-lektioner tager cirka 5–10 minutter. Hver lektion er kort og interaktiv, så du gør løbende fremskridt og kan fortsætte, hvor du slap – på både web og app.
Kan jeg skrive og køre kode i denne Java Academy-lektion?
Ja. Alle Java Academy-lektioner har en indbygget kodeeditor, så du kan skrive og køre rigtig kode direkte i din browser og få øjeblikkelig feedback fra AI – uden lokal opsætning.
Alle lektioner i dette kursus
- Kontrakterne for Iterable og Iterator
- Implementering af en brugerdefineret iterator
- ListIterator og tovejs-gennemløb
- Fail-fast vs. fail-safe-iteratorer