๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ

๋นˆ ๊ตฌ๋ฉ ์ฑ„์šฐ๊ธฐ

[Java] Fail-fast ๋ฉ”์ปค๋‹ˆ์ฆ˜

์ถœ์ฒ˜

ChatGPT


Fail-fast ๋ฉ”์ปค๋‹ˆ์ฆ˜์€ ์ž๋ฐ”์˜ ์ปฌ๋ ‰์…˜ ํ”„๋ ˆ์ž„์›Œํฌ์—์„œ ์‚ฌ์šฉ๋˜๋Š” ์ค‘์š”ํ•œ ๊ฐœ๋…์œผ๋กœ, ์ปฌ๋ ‰์…˜์ด ๋ฐ˜๋ณต๋˜๋Š” ๋™์•ˆ ๊ตฌ์กฐ์ ์œผ๋กœ ๋ณ€๊ฒฝ๋˜๋Š” ์ฆ‰์‹œ ์˜ค๋ฅ˜๋ฅผ ๋ฐœ์ƒ์‹œํ‚ค๋Š” ๋ฐฉ์‹์ด๋‹ค. ์ด ๋ฉ”์ปค๋‹ˆ์ฆ˜์€ ๋ฐ˜๋ณต ์ค‘์— ์˜ˆ๊ธฐ์น˜ ์•Š์€ ๋ฐ์ดํ„ฐ ๋ณ€์กฐ๋‚˜ ์ผ๊ด€์„ฑ ๋ฌธ์ œ๋ฅผ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ์„ค๊ณ„๋˜์—ˆ๋‹ค.

 

1. Fail-fast ๋ฉ”์ปค๋‹ˆ์ฆ˜์˜ ์ž‘๋™ ์›๋ฆฌ

์ž๋ฐ”์˜ Iterator๋Š” ์ปฌ๋ ‰์…˜์˜ ์š”์†Œ๋ฅผ ์ˆœํšŒํ•  ๋•Œ, ์ปฌ๋ ‰์…˜์˜ ๊ตฌ์กฐ๊ฐ€ ๋ณ€๊ฒฝ๋˜๋Š”์ง€ ์—ฌ๋ถ€๋ฅผ ๊ฐ์‹œํ•œ๋‹ค. ๊ตฌ์กฐ์  ๋ณ€๊ฒฝ์ด๋ž€ ์š”์†Œ์˜ ์ถ”๊ฐ€, ์‚ญ์ œ ๋˜๋Š” ์ปฌ๋ ‰์…˜์˜ ํฌ๊ธฐ์— ์˜ํ–ฅ์„ ๋ฏธ์น˜๋Š” ์ž‘์—…์„ ๋งํ•œ๋‹ค. ๋งŒ์•ฝ ์ปฌ๋ ‰์…˜์ด ๊ตฌ์กฐ์ ์œผ๋กœ ๋ณ€๊ฒฝ๋˜๋ฉด, Iterator๋Š” ConcurrentModificationException ์˜ˆ์™ธ๋ฅผ ๋˜์ ธ ์ˆœํšŒ๋ฅผ ์ฆ‰์‹œ ์ค‘๋‹จํ•œ๋‹ค.

 

์ปฌ๋ ‰์…˜์ด ๋ณ€๊ฒฝ๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด, ์ž๋ฐ”์˜ ์ปฌ๋ ‰์…˜ ํด๋ž˜์Šค๋“ค์€ ๋ชจ๋””ํŒŒ์ด ์นด์šดํŠธ(modCount)๋ผ๋Š ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. ์ด ๋ณ€์ˆ˜๋Š” ์ปฌ๋ ‰์…˜์˜ ํฌ๊ธฐ๊ฐ€ ๋ณ€๊ฒฝ๋ ใ„น ๋•Œ๋งˆ๋‹ค ์ฆ๊ฐ€ํ•œ๋‹ค.

  • Iterator๋Š” ์ปฌ๋ ‰์…˜์„ ์ƒ์„ฑํ•  ๋•Œ modCount ๊ฐ’์„ ๊ธฐ์–ตํ•œ๋‹ค.
  • ์ˆœํšŒ๊ฐ€ ์ง„ํ–‰๋˜๋Š” ๋™์•ˆ modCount๊ฐ€ ๋ณ€๊ฒฝ๋˜๋ฉด, ์ด๋Š” ์ปฌ๋ ‰์…˜์ด ์™ธ๋ถ€์—์„œ ์ˆ˜์ •๋˜์—ˆ์Œ์„ ์˜๋ฏธํ•œ๋‹ค.
  • ์ด๋•Œ Iterator๋Š” ConcurrentModificationException์„ ๋ฐœ์ƒ์‹œํ‚จ๋‹ค.

 

2. Fail-fast ๋ฉ”์ปค๋‹ˆ์ฆ˜์˜ ์˜ˆ์‹œ

๋‹ค์Œ์€ ArrayList์—์„œ fail-fast ๋ฉ”์ปค๋‹ˆ์ฆ˜์ด ์ž‘๋™ํ•˜๋Š” ์˜ˆ์‹œ์ด๋‹ค.

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class FailFastExample {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("apple");
        list.add("banana");
        list.add("cherry");

        // Iterator๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฆฌ์ŠคํŠธ๋ฅผ ์ˆœํšŒ
        Iterator<String> iterator = list.iterator();
        
        while (iterator.hasNext()) {
            String fruit = iterator.next();
            System.out.println(fruit);

            // ๋ฐ˜๋ณต ์ค‘์— ๋ฆฌ์ŠคํŠธ์—์„œ ์š”์†Œ๋ฅผ ์ง์ ‘ ์‚ญ์ œ (๋น„์ถ”์ฒœ)
            if (fruit.equals("banana")) {
                list.remove(fruit);  // ConcurrentModificationException ๋ฐœ์ƒ
            }
        }
    }
}

 

์ถœ๋ ฅ ๊ฒฐ๊ณผ

apple
banana
Exception in thread "main" java.util.ConcurrentModificationException

 

3. ์™œ fail-fast๊ฐ€ ํ•„์š”ํ•œ๊ฐ€?

1. ๋ฐ์ดํ„ฐ ์ผ๊ด€์„ฑ ๋ณด์žฅ

์ˆœํšŒ ์ค‘์— ์ปฌ๋ ‰์…˜์˜ ๊ตฌ์กฐ๊ฐ€ ๋ณ€๊ฒฝ๋˜๋ฉด, ๊ทธ ์ƒํƒœ์—์„œ ์ถ”๊ฐ€์ ์ธ ์ˆœํšŒ๊ฐ€ ์ด๋ฃจ์–ด์งˆ ๊ฒฝ์šฐ ๋ฐ์ดํ„ฐ์˜ ์ผ๊ด€์„ฑ์ด ๊นจ์งˆ ์ˆ˜ ์žˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ๋ฆฌ์ŠคํŠธ์—์„œ ์š”์†Œ๊ฐ€ ์‚ญ์ œ๋˜๋ฉด ์ธ๋ฑ์Šค๊ฐ€ ์ด๋™ํ•˜์—ฌ ๋ฐ˜๋ณต์ž์˜ ์ˆœํšŒ๊ฐ€ ๊ผฌ์ผ ์ˆ˜ ์žˆ๋‹ค. ์ด๋ฅผ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด Iterator๋Š” ์ˆœํšŒ ์ค‘์— ๊ตฌ์กฐ์ ์ธ ๋ณ€๊ฒฝ์„ ๊ฐ์ง€ํ•˜๊ณ  ์˜ˆ์™ธ๋ฅผ ๋˜์ง„๋‹ค.

2. ๋ฒ„๊ทธ ์˜ˆ๋ฐฉ

fail-fast๋Š” ์ž ์žฌ์ ์ธ ๋ฒ„๊ทธ๋ฅผ ๋นจ๋ฆฌ ๋ฐœ๊ฒฌํ•˜๊ณ  ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋„์™€์ค€๋‹ค. ์˜ˆ์™ธ๋ฅผ ๋ฐœ์ƒ์‹œ์ผœ ์ฆ‰์‹œ ๋ฌธ์ œ๋ฅผ ์•Œ๋ฆฌ๊ณ , ์ปฌ๋ ‰์…˜์„ ์ž˜๋ชป๋œ ์ƒํƒœ๋กœ ๋‚จ๊ฒจ๋‘๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•œ๋‹ค.

 

4. Fail-fast์˜ ํ•œ๊ณ„

  • fail-fast๋Š” ์Šค๋ ˆ๋“œ ์•ˆ์ •์„ฑ(Thread Safety)์„ ๋ณด์žฅํ•˜์ง€ ์•Š๋Š”๋‹ค. ์ฆ‰, ์—ฌ๋Ÿฌ ์Šค๋ ˆ๋“œ๊ฐ€ ๋™์‹œ์— ๊ฐ™์€ ์ปฌ๋ ‰์…˜์„ ์ˆ˜์ •ํ•˜๋ฉด ConcuurrentModificationException์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ๊ทธ ์ž์ฒด๋กœ ์Šค๋ ˆ๋“œ ๊ฐ„์˜ ์ถฉ๋Œ์„ ์™„์ „ํžˆ ํ•ด๊ฒฐํ•˜์ง€๋Š” ์•Š๋Š”๋‹ค.
  • ๋น„๋™๊ธฐ ํ™˜๊ฒฝ์—์„œ๋Š” fail-fast๊ฐ€ ์™„๋ฒฝํ•œ ํ•ด๊ฒฐ์ฑ…์ด ์•„๋‹ˆ๋ฉฐ, ๋Œ€์‹  ๋™๊ธฐํ™”๋œ ์ปฌ๋ ‰์…˜(Synchronized Collection) ๋˜๋Š” java.util.concurrent ํŒจํ‚ค์ง€์˜ ๋™์‹œ์„ฑ ์ปฌ๋ ‰์…˜์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, CopyOnWirteArrayList์™€ ๊ฐ™์€ ์ปฌ๋ ‰์…˜๋“ค์€ ์•ˆ์ „ํ•œ ์ˆœํšŒ๋ฅผ ์ œ๊ณตํ•œ๋‹ค.

 

5. Fail-fast๋ฅผ ํ”ผํ•˜๋Š” ๋ฐฉ๋ฒ•

ConcurrentModifcationException์„ ํ”ผํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” Iterator์˜ remove() ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด ์•ˆ์ „ํ•˜๊ฒŒ ์š”์†Œ๋ฅผ ์ œ๊ฑฐํ•ด์•ผ ํ•œ๋‹ค. ๋‹ค์Œ์€ ์˜ฌ๋ฐ”๋ฅธ ์˜ˆ์ด๋‹ค.

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class FailFastFixedExample {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("apple");
        list.add("banana");
        list.add("cherry");

        // Iterator๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฆฌ์ŠคํŠธ๋ฅผ ์ˆœํšŒ
        Iterator<String> iterator = list.iterator();
        
        while (iterator.hasNext()) {
            String fruit = iterator.next();
            System.out.println(fruit);

            // Iterator์˜ remove() ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์•ˆ์ „ํ•˜๊ฒŒ ์š”์†Œ ์‚ญ์ œ
            if (fruit.equals("banana")) {
                iterator.remove();
            }
        }

        System.out.println("List after removal: " + list);
    }
}

 

์ถœ๋ ฅ ๊ฒฐ๊ณผ

apple
banana
cherry
List after removal: [apple, cherry]

 

์ด ์ฝ”๋“œ์—์„œ๋Š” Iterator์˜ remove() ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด ์•ˆ์ „ํ•˜๊ฒŒ "banana" ์š”์†Œ๋ฅผ ์ œ๊ฑฐํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด ๊ฒฝ์šฐ ConcurrentModificationException์ด ๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š”๋‹ค.

 

6. Fail-safe ์ปฌ๋ ‰์…˜

Fail-safe ์ปฌ๋ ‰์…˜์€ fail-fast์™€ ๋ฐ˜๋Œ€๋˜๋Š” ๊ฐœ๋…์œผ๋กœ, ๋ฐ˜๋ณต ์ค‘์— ์ปฌ๋ ‰์…˜์ด ์ˆ˜์ •๋˜๋”๋ผ๋„ ์˜ˆ์™ธ๋ฅผ ๋˜์ง€์ง€ ์•Š๊ณ  ์•ˆ์ „ํ•˜๊ฒŒ ๋™์ž‘ํ•˜๋Š” ์ปฌ๋ ‰์…˜์ด๋‹ค. ์ž๋ฐ”์˜ ๋™์‹œ์„ฑ ์ปฌ๋ ‰์…˜(concurrent collections)์ด ๋Œ€ํ‘œ์ ์ธ ์˜ˆ๋กœ, CopyOnWriteArrayList, ConcurrentHashMap ๋“ฑ์€ ์ปฌ๋ ‰์…˜์„ ์•ˆ์ „ํ•˜๊ฒŒ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.

 

์˜ˆ๋ฅผ ๋“ค์–ด, CopyOnWirteArrayList๋Š” ๋ฆฌ์ŠคํŠธ๋ฅผ ์ˆ˜์ •ํ•  ๋•Œ๋งˆ๋‹ค ์ƒˆ๋กœ์šด ๋ณต์‚ฌ๋ณธ์„ ์ƒ์„ฑํ•ด ์•ˆ์ „ํ•˜๊ฒŒ ์ˆœํšŒ๋ฅผ ์ง€์›ํ•œ๋‹ค.

import java.util.concurrent.CopyOnWriteArrayList;
import java.util.Iterator;

public class FailSafeExample {
    public static void main(String[] args) {
        CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
        list.add("apple");
        list.add("banana");
        list.add("cherry");

        Iterator<String> iterator = list.iterator();
        while (iterator.hasNext()) {
            String fruit = iterator.next();
            System.out.println(fruit);

            // ์ˆœํšŒ ์ค‘์— ๋ฆฌ์ŠคํŠธ์—์„œ ์•ˆ์ „ํ•˜๊ฒŒ ์š”์†Œ๋ฅผ ์ถ”๊ฐ€
            if (fruit.equals("banana")) {
                list.add("orange");  // ์˜ˆ์™ธ ๋ฐœ์ƒํ•˜์ง€ ์•Š์Œ
            }
        }

        System.out.println("List after modification: " + list);
    }
}

 

์ถœ๋ ฅ ๊ฒฐ๊ณผ

apple
banana
cherry
List after modification: [apple, banana, cherry, orange]

 

CopyOnWirteArrayList๋Š” ์ˆœํšŒ ์ค‘์—๋„ ์•ˆ์ „ํ•˜๊ฒŒ ์ปฌ๋ ‰์…˜์„ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ๋‹ค. ์š”์†Œ๊ฐ€ ์ถ”๊ฐ€๋˜์—ˆ์Œ์—๋„ ๋ถˆ๊ตฌํ•˜๊ณ  ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š”๋‹ค.

 

๊ฒฐ๋ก 

Fail-fast ๋ฉ”์ปค๋‹ˆ์ฆ˜์€ ์ž๋ฐ”์˜ ์ปฌ๋ ‰์…˜์—์„œ ๊ตฌ์กฐ์  ๋ณ€๊ฒฝ์„ ๊ฐ์ง€ํ•˜๊ณ , ๋ณ€๊ฒฝ์ด ๋ฐœ์ƒํ•˜๋ฉด ์ฆ‰์‹œ ConcurrentModificationException์„ ๋˜์ ธ ๋ฌธ์ œ๋ฅผ ๋น ๋ฅด๊ฒŒ ์•Œ๋ฆฌ๋Š” ๋ฐฉ์‹์ด๋‹ค. ์ด๋Š” ๋ฐ์ดํ„ฐ ์ผ๊ด€์„ฑ์„ ์œ ์ง€ํ•˜๊ณ , ์ฝ”๋“œ์˜ ๋ฒ„๊ทธ๋ฅผ ์กฐ๊ธฐ์— ๋ฐœ๊ฒฌํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ค€๋‹ค. ํ•˜์ง€๋งŒ ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋“œ ํ™˜๊ฒฝ์—์„œ๋Š” fail-fast ๋ฉ”์ปค๋‹ˆ์ฆ˜์ด ์™„๋ฒฝํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ, ๋™์‹œ์„ฑ ์ปฌ๋ ‰์…˜์„ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜ ์ ์ ˆํ•œ ๋™๊ธฐํ™”๊ฐ€ ํ•„์š”ํ•˜๋‹ค.