0. ํ์ต ๋ชฉ์
iterator.next()๋ฅผ ํ์ฉํ์ฌ Collections์ ์๋ฃ๊ตฌ์กฐ๋ค์ ์ํํ๋ฉด์, ์๋ฃ ๊ตฌ์กฐ ๋ด์ ๊ฐ๋ค์ ์์ ํ๋ ค๊ณ ํ๋ฉด ConcurrntModificationException์ด ๋๋ ์ด์ ๋ฅผ ์ดํดํ๋ค.Fail-Fast Interator์Fail-Safe Iterator์ ์ฐจ์ด๋ฅผ ์ดํดํ๋ค.
1. Fail-Fast ์ ๋ต๊ณผ Fail-Safe ์ ๋ต
Fail-Fast ์ ๋ต ์ ์์คํ
์๋ ์ค ์คํจ ์ง์ ์ด ์๊ธฐ๋ฉด ๊ทธ ์ฆ์ ๋ชจ๋ ์๋์ ๋ฉ์ถ๋ ์ ๋ต์ด๋ค. ์ด๋ ์คํจ ์ง์ ์ ๋ํ ๋ช
ํํ ํ๋จ๊ณผ ์ผ๊ด์ฑ ์ ์ง๋ฅผ ๋ชฉํ๋ก ํ๋ค.Fail-Safe ์ ๋ต ์ ์์คํ
์๋ ์ค ์คํจ๋ฅผ ๋ง๋ฑ๋จ๋ฆฐ ์ํฉ์๋ ์๋์ ์ค๋จํ์ง ์๋๋ค. ๋ค๋ง ์ต๋ํ ์คํจ๊ฐ ์ปค์ง์ง ์๋ ๋ฐฉํฅ์ผ๋ก ๋์๊ฐ๋ ์ ๋ต์ด๋ค.
Java๋ Iterator ํจํด์ Collection ์ฐํ ์๋ฃ๊ตฌ์กฐ์ ์ ์ฉํจ์ ์์ด ์ด ๋๊ฐ์ง ์ ๋ต์ ๋ชจ๋ ์ฌ์ฉํ์ฌ ๊ตฌํํ์๋ค. ๋ฐ๋ผ์ Java์ Iterator์ ์ข
๋ฅ๋ Fail-Fast Iterators ์ Fail-Safe Iterators๋ก ๋๋๋ค.
2. Fail-Fast Iterators
(1) ์ ์
Fail-Fast Iterators ๋ ๊ทธ๊ฒ์ด ํ์ฌ ์ํํ๊ณ ์๋ ์๋ฃ๊ตฌ์กฐ์ '๊ตฌ์กฐ์ ์์ ' ์ด ์ผ์ด๋ฌ์ ๊ฒฝ์ฐ '์ต๋ํ' ConcurrencyModificationException์ ๋ฐ์์ํค๋ Iterator์ด๋ค.
์ฌ๊ธฐ์ ๋ ๊ฐ์ง ์ฌํญ์ ๊ฐ์กฐํ๋๋ฐ ๋ฐ๋ก '๊ตฌ์กฐ์ ์์ ' ๊ณผ '์ต๋ํ' ์ด๋ค.
A. ๊ตฌ์กฐ์ ์์
๊ตฌ์กฐ์ ์์ ์ด๋ ๋ง ๊ทธ๋๋ก ์๋ฃ๊ตฌ์กฐ์ ๊ตฌ์กฐ์ ์ธ ๋ณํ๊ฐ ์๊ธฐ๋ ์์ ์ด๋ค. ์ฆ ํน์ ๊ฐ์ ์ฝ์
ํ๊ฑฐ๋ ์ญ์ ํด์ ์ค์ง์ ์ผ๋ก ์๋ฃ๊ตฌ์กฐ์ ํฌ๊ธฐ์ ๋ณํ๋ฅผ ์ฃผ๋ ์์
์ ๋งํ๋ค.
public static void main(String[] args) throws IOException {
HashMap<Integer, Integer> map = new HashMap<>();
map.put(1,1);
map.put(2,2);
Iterator<Integer> iter = map.keySet().iterator();
while (iter.hasNext()){
map.put(1,12324);
System.out.println(map.get(iter.next()));
map.put(2,4);
}
}
์๋ฅผ ๋ค์ด ์์ ์์ ์ HashMap์ ํน์ Key์ value๋ฅผ ๋ฐ๊พธ๋ ์์ ์ด๊ธฐ์ ๊ตฌ์กฐ์ ์์ ์ด๋ผ ๋ณผ ์ ์๋ค. ๋ฐ๋ผ์ Iterator ์์ ๋์ค ์์ ๊ฐ์ ์์ ์ ๊ฐํด๋ ์ค๋ฅ์์ด ๋์ํ๋ค.
public static void main(String[] args) throws IOException {
HashMap<Integer, Integer> map = new HashMap<>();
map.put(1,1);
map.put(2,2);
Iterator<Integer> iter = map.keySet().iterator();
while (iter.hasNext()){
map.remove(1);
System.out.println(map.get(iter.next()));
map.put(3,2);
}
}
๋ฐ๋ฉด ์์ ์์ ์ ๊ตฌ์กฐ์ ๋ณํ๋ฅผ ์ผ์ผํจ๋ค. ๋ฐ๋ผ์ ๋ค์๊ณผ ๊ฐ์ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ค.
B. '์ต๋ํ' ์ค๋ฅ๋ฅผ ๋ฐ์
The Fail-Fast behavior isn't guaranteed to happen in all scenarios as it's impossible to predict behavior in case of concurrent modifications. These iterators throw ConcurrentModificationException on a best effort basis.
์๋ Baedung ์ ๋์จ Fail-Fast์ ๋ํ ์ค๋ช ์ด๋ค. ์ ์ค๋ช ๋๋ก ๋์ ์์ ์ฌํญ์ด ์๊ธฐ๋ ๊ฒ์ ์์ธกํ ์ ์๊ธฐ ๋๋ฌธ์, ๊ตฌ์กฐ์ ๋์ ์์ ์ด ์๊ธฐ๋ ๋ชจ๋ ์๋๋ฆฌ์ค์ ์์ธ๊ฐ ๋ฐ์ํ๋ค๋ ๊ฒ์ ๋ณด์ฅํ ์๊ฐ ์๋ค. ๋ฐ๋ผ์ '์ต๋ํ' ์ด๋ผ๋ ํํ์ ์ผ๋ค.
(2) ๋์ ๊ณผ์
Iterator.next()๋ก ์๋ฃ๊ตฌ์กฐ ์ํ ์์
๋ณธ๊ฒฉ์ ์ผ๋ก ๋ค์ index๋ฅผ ๋ฐ๋ผ๋ณด๊ธฐ ์ ์ iterator๊ฐ ์๋ ์ค์ธ ์๋ฃ๊ตฌ์กฐ์
modCountํ์ธ๋ง์ฝ ํด๋น ๊ฐ์ด ์ํ ์์ ์ ์ด๋ ๋น๊ตํด์ ๋ฐ๋์ด ์์ผ๋ฉด ๋ฐ๋ก ConcurrencyModificationException ๋ฐ์์ํด.
Collections๋ฅผ ์์๋ฐ์ ๋ชจ๋ ์๋ฃ ๊ตฌ์กฐ๋ค์ ๋ด๋ถ์
modCount๋ผ๋ int ํ ๋ณ์๋ฅผ ๊ฐ์ง๋ค. ํด๋น ๋ณ์๋ ์๋ฃ๊ตฌ์กฐ์ ๊ตฌ์กฐ์ ๋ณ๊ฒฝ (์ฝ์ ํน์ ์ญ์ )๊ฐ ์๊ฒผ์ ๋๋ง๋ค count๊ฐ 1์ฉ ์ค๋ฅธ๋ค.
๋ง์ฝ ํน์ Fail-Fast ์ ๋ต์ ๋ฐ๋ฅด๋ ์๋ฃ๊ตฌ์กฐ์ Iterator ๊ตฌํ์ฒด๋ฅผ ๋ง๋ค์๋ค๋ฉด, Iterator๋ ์ํํ๊ธฐ ์ํด iterator.next() ๋ผ๋ ํจ์๋ฅผ ์ฌ์ฉํ ๊ฒ์ด๋ค. ํด๋น ํจ์๋ ์ค์ ์๋์ ํ๊ธฐ ์ ์ modcount๊ฐ ์ํ ์ ์ด๋ ๊ฐ์ด ๋ฐ๋์๋์ง ํ์ธํ๋ค. ๋ง์ฝ ๋ฐ๋์ด ์์ผ๋ฉด ๊ทธ ์๋ฆฌ์์ ConCurrencyModificationException์ ๋ฐ์์ํค๊ณ ์๋์ ์ค์งํ๋ค.
(3) ์ข ๋ฅ
์ฐ๋ฆฌ๊ฐ ์๊ณ ๋ฆฌ์ฆ์ ํ๋ฉฐ ์ฐ๋ ์๋ฃ๊ตฌ์กฐ๋ค (์ฆ java.util.package ์ ์๋ Collections ์๋ฃ๊ตฌ์กฐ)์ ๋ชจ๋ Fail-Fast ์ ๋ต์ ์ฌ์ฉํ๊ณ ์๋ค.
(4) ๊ทธ๋ผ์๋ ์๋ฃ๊ตฌ์กฐ๋ฅผ ์ํํ๋ฉด์ ์ญ์ ํด์ผํ ๊ฒฝ์ฐ ์ด๋ป๊ฒ ํ ๊น?
์ด ๊ฒฝ์ฐ์๋ ํด๋น ์๋ฃ๊ตฌ์กฐ์ remove ๋์ ์ Iterator์ remove๋ฅผ ์ฌ์ฉํ๋ฉด ๋๋ค.
Iterator์ ๋ฉค๋ฒ ํจ์ remove๋ ๋์์์ ์์ธ๋ฅผ ์ฐํํ๋ฉฐ ์ํ์ ๋์์ ์ญ์ ๋ฅผ ํ ์ ์๋ ๋งค์ปค๋์ฆ์ ๊ฐ์ง๊ณ ์๊ธฐ ๋๋ฌธ์ด๋ค.
A. iterator.remove() ๋ด๋ถ ๋ฉ์ปค๋์ฆ
์ด๋ Iterator๋ฅผ ๊ตฌํํ ๊ฐ ์๋ฃ๊ตฌ์กฐ์ ๊ตฌํ์ฒด๋ง๋ค ์กฐ๊ธ์ฉ ๋ค๋ฅด๋ค. ๊ฐ์ฅ ๊ฐ๋จํ๊ฒ ArrayList์ Iterator ๊ตฌํ์ฒด๋ฅผ ์์๋ก ๋ค์ด๋ณด๊ฒ ๋ค.
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount; // modCount๋ ์๋ฃ๊ตฌ์กฐ์ธ ArrayList์๊ฒ ๋ฌ๋ ค์์.
// prevent creating a synthetic constructor
Itr() {}
public boolean hasNext() {
return cursor != size;
}
@SuppressWarnings("unchecked")
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
๋จผ์ ์ฃผ๋ชฉํด์ผํ 3๊ฐ์ง ๋ณ์๊ฐ ์๋๋ฐ, ๋ฐ๋ก cursor์ lastRet, expectedModCount์ด๋ค. cursor๋ ํ์ฌ ๋ฐฉ๋ฌธํ index์ด๊ณ lastRet์ ์ด์ ์ ๋ฐฉ๋ฌธํ index์ด๋ค. expectedModCount๋ ์ํ ์ด์ ์ modCount๊ฐ์ ์ ์ฅํ์ฌ ์ํ๋ฅผ ํ๋ฉด์ modCount๊ฐ ์ค์ ๋ก ๋ฐ๋์๋์ง ์ฒดํฌ๋ฅผ ํ๋ ๋ณ์์ด๋ค.
iterator.remove()๋ ํ์ฌ ๋ฐ๋ผ๋ณด๊ณ ์๋ ์ธ๋ฑ์ค๋ฅผ ์ญ์ ํ๋ค. ์ญ์ ์ดํ์๋ cursor๋ฅผ ๋ค์ lastRet์ผ๋ก ๋ง์ถ๋ค. ์ญ์ ๋ก ์ธํด ArrayList ์์ฒด modCount ๊ฐ์ด ๋ณ๊ฒฝ ๋์์ ๊ฒ์์ผ๋ก, expectedModCount๋ฅผ ๋ฐ๋ ๊ฐ๊ณผ ๋๊ธฐํ ์ํจ๋ค.
์์ ๊ฐ์ ๊ณผ์ ์ผ๋ก ๋์ ์์ ์ค๋ฅ๋ฅผ ํผํ๋ค.
๋ค์์ ๋์ ์์ ์ค๋ฅ๊ฐ ๋๋ ์ญ์ ์ ์ ๋๋ ์ญ์ ์ ๋ํ ์์์ ํจ๊ป Fail-Fast Iterator์ ๋ํ ์ค๋ช ์ ๋ง์น๊ฒ ๋ค.
ArrayList<Integer> numbers = // ...
Iterator<Integer> iterator = numbers.iterator();
while (iterator.hasNext()) {
if (iterator.next() == 30) {
iterator.remove(); // ok!
}
}
iterator = numbers.iterator();
while (iterator.hasNext()) {
if (iterator.next() == 40) {
numbers.remove(2); // exception
}
}
3. Fail-Safe Iterators
(1) ์ ์
Fail-Safe Iterators ๋ ์ํ ๋์ค์ ๊ตฌ์กฐ์ ๋ณ๊ฒฝ์ด ์ผ์ด๋๋ ๋์์ฑ ๋ฌธ์ ๊ฐ ๋ฐ์ํด๋ ์์ธ๋ฅผ ๋ฐ์์ํค์ง ์๋ ๊ฒ์ ํํ ์ ๋ต์ด๋ค. ๋ค๋ง ๋ฌธ์ ์ํฉ์ด ์ปค์ง๋ ๊ฒ์ ํผํด์ ์์คํ
์ ์ฒด๊ฐ ์ต๋ํ ์ ์ ์๋ํ๋๋ก ์ ๋ํ๋ค.
(2) ๊ตฌํ ์ ๋ต
Java์์ ํด๋น ์ ๋ต์ ๊ตฌํํ ๋ฐฉ๋ฒ์๋ ๋ ๊ฐ์ง๊ฐ ์๋ค.
- ๋ณต์ฌํด์ ์ฌ์ฉํ๊ธฐ (Copy-On-Write)
- ์ฝํ ์ผ๊ด์ฑ (Weakly-Consistent)
A. Copy on Write
ํด๋น ๊ตฌํ ๋ฐฉ๋ฒ์ Iterator๊ฐ ๋ณต์ฌ๋ณธ์์ ์ํํ๋๋ก ๋ง๋ ๋ค. ๋ฐ๋ผ์ ๋ณธ ์๋ฃ๊ตฌ์กฐ์์ ๋ณ๊ฒฝ ์ฌํญ์ด ์๊ฒจ๋, ์ํ ๊ณผ์ ์์ฒด๋ ๋ณ๊ฒฝ ์ฌํญ์ด ๋ฐ์๋์ง ์๊ธฐ ๋๋ฌธ์, ์ค๋ฅ ์์ด ์์ ์ ๋๋ผ ์ ์๋ค.
- ์ฅ์ : ๋ฉํฐ ์ค๋ ๋ ํ๊ฒฝ์์ readOnly ์์ ์ ํ ๊ฒฝ์ฐ ์ฐ๊ธฐ ์ ํฉํ๋ค.
- ๋จ์ :
- ๋งค๋ฒ ๋ณต์ฌํ๊ธฐ ๋๋ฌธ์ ๊ธฐ๋ฅ ์ฒ๋ฆฌ์ ๋ํ CPU ๋น์ฉ์ด ๋๊ณ , ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋์ด ์ฆ๊ฐํ๋ค.
- ์์์ ๋งํ๋๋ก ์ํ ๋์ค ์๊ธด ๋ณ๊ฒฝ ์ฌํญ์ ํ์ธ์ด ๋ถ๊ฐ๋ฅํ๋ค.
ํด๋น ๋ฐฉ๋ฒ์ ์ฌ์ฉํด ๊ตฌํํ ์๋ฃ๊ตฌ์กฐ์๋ CopyOnWirteArrayList๊ฐ ์๋ค.
B. Weakly-Consistent
ํด๋น ๊ตฌํ ๋ฐฉ๋ฒ์ ๊ตฌ์กฐ์ ๋ณ๊ฒฝ์ด ์งํ๋ ๋, ๋ณ๊ฒฝ๋๋ '๋ถ๋ถ์๋ง ๋ถํ CAS ์ธ๊ทธ๋จผํธ ๋ฝ' ์ ๊ฑธ์ด์ ๋์์ฑ ๋ฌธ์ ์์ด ๋ชจ๋ ์ค๋ ๋์์ ์๋ฃ ๊ตฌ์กฐ์ ๋ด์ฉ์ด ๋๊ธฐํ๋๋๋ก ํ๋ ๋ฐฉ๋ฒ์ด๋ค.
- ์ฅ์ : ์ํ ๋์ค์ ์๊ธด ๋ณ๊ฒฝ ์ฌํญ์ ์ต๋ํ ๋ฐ์
a. ์ง์ญ์ ๋ฝ์ ์ด๋ป๊ฒ ๋์ํ๋?
์๋ฅผ ๋ค์ด ConcurentHashMap์ ๊ฒฝ์ฐ ๊ตฌ์กฐ์ ๋ณ๊ฒฝ์ด ์ผ์ด๋๋ ๋ฒํท์๋ง Lock์ ๊ฑด๋ค. ๋ฐ๋ผ์ ๋๋จธ์ง bucket์ ์ฌ์ ํ ์ ๊ทผ ๊ฐ๋ฅํ ์ํ๊ฐ ๋๋ค.
b. ์ํ ๋์ค์ ์๊ธด ๋ณ๊ฒฝ ์ฌํญ์ ์ต๋ํ ๋ฐ์
iterator๊ฐ ์ํํ๊ธฐ ์ ์ ์๊ธด ๋ณ๊ฒฝ์ฌํญ์ ์ต๋ํ ๋ฐ์ํด์ ์งํํ๋ค.
public static void main(String[] args) throws IOException {
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
map.put("First", 10);
map.put("Second", 20);
map.put("Third", 30);
map.put("Fourth", 40);
Iterator<String> iterator = map.keySet().iterator();
int i = 1;
while (iterator.hasNext()) {
map.put("Fifth", 50);
String key = iterator.next();
System.out.println(i++);
}
}
์ด๊ฑด ConcurrentHashMap์ด 5๋ฒ ๋๋ค.
ํด๋น ๋ฐฉ๋ฒ์ผ๋ก ๊ตฌํํ ์๋ฃ๊ตฌ์กฐ์๋ ConcurrentHashMap, ConcurrentLinkedQueue ๊ฐ ์๋ค.
4. ํต์ฌ ์์ฝ
fail-fast iterator๋ ์ํ ๋์ค ๊ตฌ์กฐ์ ๋ณ๊ฒฝ์ด ์ผ์ด๋ ์ฆ์ ํด๋น ์๋ฃ๊ตฌ์กฐ์ ์๋์ ๋ฉ์ถ๋ iterator์ด๋ค. ๋ฐ์ดํฐ์ ์ ํฉ์ฑ๊ณผ ์ผ๊ด์ฑ์ ์ค์ํ๋ ์ ๋ต์ด๋ค.fail-safe iterator๋ ์ํ ๋์ค์ ๊ตฌ์กฐ์ ๋ณ๊ฒฝ์ด ์ผ์ด๋๋ ์ค๋ฅ๋ฅผ ๋ฐ์์ํค์ง ์๊ณ ํด๋น ์์ ์ ์งํํ๋ค. ๋ณ๊ฒฝ ์ฌํญ์ ์์ ๋ฐ์๋์ง ์๊ฑฐ๋ (copy-on-write), ์๋๋ฉด ์ต๋ํ ๋ฐ์(weakly-consistence)ํ๋ ์ ๋ต์ ํํ๋ค.
๋ถ๋ก
A. ๋ชจ๋ฅด๋ ๋จ์ด ์ ๋ฆฌ
play along with ~~
: (์๋๋ฐฉ์ด ์ํ๋ ๋๋ก) ๋ง์ถฐ ์ฃผ๋ค. ๋์กฐํด์ฃผ๋ค. ํ์กฐํ๋ค.
(Fail-fast iterators in Java don't play along when the underlying collection gets modified.)
(์๋ฐ์ ๋น ๋ฅธ ์คํจ ์ดํฐ๋ ์ดํฐ๋ ๊ทธ๊ฒ์ด ๋์๊ฐ๊ณ ์๋ ์ปฌ๋ ์ ์ด ์์ ๋์์ ๋ ์์ํ ํ์กฐํด์ฃผ์ง ์๋๋ค.)abort
: ์ค๋จํ๋ค.
(Fail-Fast systems abort operation as-fast-as-possible exposing failures immediately and stopping the whole operation.)
๋น ๋ฅธ ์คํจ ์์คํ ์ ์คํจ์ ๋ ธ์ถ๋๋ ์ฆ์ ์๋์ ์ค๋จํ๋ค. ๊ทธ๋ฆฌ๊ณ ๋ชจ๋ ์๋์ ์ค๋จํ๋ค.basis
: ๊ธฐ์ค, ๋ฐฉ์, ๋ฐฉ๋ฒ
(These iterators throw ConcurrentModificationException on a best effort basis.)
(์ดํฐ๋ ์ดํฐ๋ ์ต์ ์ ๋คํด ๋ ธ๋ ฅํด์ ๋์์์ ์ค๋ฅ๋ฅผ ์ผ์ผํจ๋ค.)favor A over B
: A๋ฅผ B๋ณด๋ค ์ ํธํ๋ค.
(Faile-Safe Iterator favor lack of failure over the inconvenience of exception handling)
(์คํจ-์์ ์ดํฐ๋ ์ดํฐ๋ ์์ธ ํธ๋ค๋ง์ ๋ถํธ๋ณด๋ค ์คํจ๊ฐ ์๋ ๊ฒ์ ๋ ์ ํธํ๋ค.)