item 79 sijun - JAVA-JIKIMI/EFFECTIVE-JAVA3 GitHub Wiki

[์•„์ดํ…œ 79] ๊ณผ๋„ํ•œ ๋™๊ธฐํ™”๋Š” ํ”ผํ•˜๋ผ

๊ณผ๋„ํ•œ ๋™๊ธฐํ™”๋Š” ์„ฑ๋Šฅ์„ ๋–จ์–ด๋œจ๋ฆฌ๊ณ , ๊ต์ฐฉ์ƒํƒœ์— ๋น ๋œจ๋ฆฌ๊ณ , ์‹ฌ์ง€์–ด ์˜ˆ์ธกํ•  ์ˆ˜ ์—†๋Š” ๋™์ž‘์„ ๋‚ณ๊ธฐ๋„ ํ•œ๋‹ค.

์‘๋‹ต ๋ถˆ๊ฐ€์™€ ์•ˆ์ „ ์‹คํŒจ๋ฅผ ํ”ผํ•˜๋ ค๋ฉด ๋™๊ธฐํ™” ๋ฉ”์„œ๋“œ๋‚˜ ๋™๊ธฐํ™” ๋ธ”๋ก ์•ˆ์—์„œ๋Š” ์ œ์–ด๋ฅผ ์ ˆ๋Œ€๋กœ ํด๋ผ์ด์–ธํŠธ์— ์–‘๋„ํ•˜๋ฉด ์•ˆ ๋œ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ๋™๊ธฐํ™”๋œ ์˜์—ญ ์•ˆ์—์„œ๋Š” ์žฌ์ •์˜ํ•  ์ˆ˜ ์žˆ๋Š” ๋ฉ”์„œ๋“œ๋Š” ํ˜ธ์ถœํ•˜๋ฉด ์•ˆ ๋˜๋ฉฐ, ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๋„˜๊ฒจ์ค€ ํ•จ์ˆ˜ ๊ฐ์ฒด(์•„์ดํ…œ 24)๋ฅผ ํ˜ธ์ถœํ•ด์„œ๋„ ์•ˆ ๋œ๋‹ค. ์ด๋Š” ์˜ˆ์™ธ๋ฅผ ์ผ์œผํ‚ค๊ฑฐ๋‚˜ ๊ต์ฐฉ์ƒํƒœ์— ๋น ์ง€๊ฑฐ๋‚˜ ๋ฐ์ดํ„ฐ๋ฅผ ํ›ผ์†ํ•  ์ˆ˜๋„ ์žˆ๋‹ค.

๋‹ค์Œ์˜ ์˜ˆ์‹œ ์ฝ”๋“œ์ด๋‹ค.

public class ObservableSet<E> extends ForwardingSet<E> {

    public ObservableSet(Set<E> set) {
        super(set);
    }

    private final List<SetObserver<E>> observers = new ArrayList<>();

    public void addObserver(SetObserver<E> observer) {
        synchronized (observers) {
            observers.add(observer);
        }
    }
    
    public boolean removeObserver(SetObserver<E> observer) {
        synchronized (observers) {
            return observers.remove(observer);
        }
    }
    
    private void notifyElementAdded(E element) {
        synchronized (observers) {
            for(SetObserver<E> observer : observers) {
                observer.added(this, element);
            }
        }
    }
    
    @Override
    public boolean add(E element) {
        boolean added = super.add(element);
        if(added) {
            notifyElementAdded(element);
        }
        return added;
    }

    @Override
    public boolean addAll(Collection<? extends E> c) {
        boolean result = false;
        for (E element : c) {
            result |= add(element); //notifyElementAdded๋ฅผ ํ˜ธ์ถœ
        }
        return result;
    }
}

์œ„ ์ฝ”๋“œ์—์„œ ๊ด€์ฐฐ์ž๋“ค์€ addObserver์™€ removeObserver ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•ด ๊ตฌ๋…์„ ์‹ ์ฒญํ•˜๊ฑฐ๋‚˜ ํ•ด์ง€ํ•œ๋‹ค. ๋‘ ๊ฒฝ์šฐ ๋ชจ๋‘ ๋‹ค์Œ ์ฝœ๋ฐฑ ์ธํ„ฐํŽ˜์ด์Šค์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ๋ฉ”์„œ๋“œ์— ๊ฑด๋‚ธ๋‹ค.

@FunctionalInteface public interface SetObserver<E> {
	// ObservableSet์— ์›์†Œ๊ฐ€ ๋”ํ•ด์ง€๋ฉด ํ˜ธ์ถœ๋œ๋‹ค.
	void added(ObservableSet<E> set, E element);
}

๋ˆˆ์œผ๋กœ ๋ณด๊ธฐ์—๋Š” ObservableSet์€ ์ž˜ ๋™์ž‘ํ•  ๊ฒƒ ๊ฐ™๋‹ค. ์˜ˆ์ปจ๋Œ€ ๋‹ค์Œ ํ”„๋กœ๊ทธ๋žจ์€ 0๋ถ€ํ„ฐ 99๊นŒ์ง€๋ฅผ ์ถœ๋ ฅํ•œ๋‹ค.

public static void main(String[] args) {
	ObservableSet<Integer> set =
				new ObservableSet<>(new HashSet<>());

	set.addObserver((s, e) -> System.out.println(e));

	for (int i = 0; i < 100; i++)
		set.add(i);
}

์˜ˆ์™ธ ๋ฐœ์ƒ

์•„๋ž˜์™€ ๊ฐ™์ด ์ฝ”๋“œ ์ถ”๊ฐ€ ์‹œ, ๊ทธ ๊ฐ’์ด 23์ด๋ฉด ์ž๊ธฐ ์ž์‹ ์„ ์ œ๊ฑฐ(๊ตฌ๋…ํ•ด์ง€)ํ•˜๋Š” ๊ด€์ฐฐ์ž๋ฅผ ์ถ”๊ฐ€ํ•ด๋ณด์ž.

๊ด€์ฐฐ์ž ์ž์‹ ์ด ๊ตฌ๋…ํ•ด์ง€ ๋  ๊ฑฐ๋ผ ์˜ˆ์ƒ๋˜์ง€๋งŒ, ConcurrentModificationException์„ ๋˜์ง„๋‹ค. ๊ด€์ฐฐ์ž์˜ added ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ์ด ์ผ์–ด๋‚œ ์‹œ์ ์ด notifyElementAdded๊ฐ€ ๊ด€์ฐฐ์ž๋“ค์˜ ๋ฆฌ์ŠคํŠธ๋ฅผ ์ˆœํšŒํ•˜๋Š” ๋„์ค‘์ด๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

๋ฆฌ์ŠคํŠธ์—์„œ ์›์†Œ๋ฅผ ์ œ๊ฑฐํ•˜๋ ค ํ•˜๋Š”๋ฐ, ๋งˆ์นจ ์ง€๊ธˆ์€ ์ด ๋ฆฌ์ŠคํŠธ๋ฅผ ์ˆœํšŒํ•˜๋Š” ๋„์ค‘์ด๋ผ ํ—ˆ์šฉ๋˜์ง€ ์•Š๋Š” ๋™์ž‘์ด๋‹ค.

public static void main(String[] args) {
    ObservableSet<Integer> set =
            new ObservableSet<>(new HashSet<>());

    set.addObserver(new SetObserver<>() {
        public void added(ObservableSet<Integer> s, Integer e) {
            System.out.println(e);
            if (e == 23)
                s.removeObserver(this);
        }
    });

    for (int i = 0; i < 100; i++)
        set.add(i);
}

๊ต์ฐฉ ์ƒํƒœ ๋ฐœ์ƒ

์ด๋ฒˆ์—๋Š” ๊ตฌ๋…ํ•ด์ง€๋ฅผ ํ•˜๋Š” ๊ด€์ฐฐ์ž๋ฅผ ์ž‘์„ฑํ•˜๋Š”๋ฐ, removeObserver๋ฅผ ์ง์ ‘ ํ˜ธ์ถœํ•˜์ง€ ์•Š๊ณ  ์‹คํ–‰์ž ์„œ๋น„์Šค(ExecutorService)๋ฅผ ์‚ฌ์šฉํ•ด ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œํ•œํ…Œ ๋ถ€ํƒํ•  ๊ฒƒ์ด๋‹ค.

set.addObserver(new SetObserver<>() {
    public void added(ObservableSet<Integer> s, Integer e) {
        System.out.println(e);
        if (e == 23) {
            ExecutorService exec =
                    Executors.newSingleThreadExecutor();
            try {
                exec.submit(() -> s.removeObserver(this)).get();
            } catch (ExecutionException | InterruptedException ex) {
                throw new AssertionError(ex);
            } finally {
                exec.shutdown();
            }
        }
    }
});

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

๋‹คํ–‰์ด ์ด๋Ÿฐ ๋ฌธ์ œ๋“ค์€ ๋Œ€๋ถ€๋ถ„ ์–ด๋ ต์ง€ ์•Š๊ฒŒ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค. ์™ธ๊ณ„์ธ ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ์„ ๋™๊ธฐํ™” ๋ธ”๋ก ๋ฐ”๊นฅ์œผ๋กœ ์˜ฎ๊ธฐ๋ฉด ๋œ๋‹ค. notifyElementAdded ๋ฉ”์„œ๋“œ์—์„œ๋ผ๋ฉด ๊ด€์ฐฐ์ž ๋ฆฌ์ŠคํŠธ๋ฅผ ๋ณต์‚ฌํ•ด ์“ฐ๋ฉด ๋ฝ ์—†์ด๋„ ์•ˆ์ „ํ•˜๊ฒŒ ์ˆœํšŒํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด ๋ฐฉ์‹์„ ์ ์šฉํ•˜๋ฉด ์•ž์„œ์˜ ๋‘ ์˜ˆ์ œ์—์„œ ์˜ˆ์™ธ ๋ฐœ์ƒ๊ณผ ๊ต์ฐฉ์ƒํƒœ ์ฆ์ƒ์ด ์‚ฌ๋ผ์ง„๋‹ค.

๋ณต์‚ฌํ•˜์—ฌ ์‚ฌ์šฉํ•˜๋Š” ํ•ด๊ฒฐ๋ฐฉ๋ฒ•

private void notifyElementAdded(E element) {
   List<SetObserver<E>> snapshot = null;
   synchronized(observers) {
       snapshot = new ArrayList<>(observers);
   }
   for (SetObserver<E> observer : snapshot)
       observer.added(this, element);
}

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

CopyOnWriteArrayList๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ํ•ด๊ฒฐ๋ฐฉ๋ฒ•

private final List<SetObserver<E>> observers = new CopyOnWriteArrayList<>();

public void addObserver(SetObserver<E> observer) {
	observers.add(observer);
}

public boolean removeObserver(SetObserver<E> observer) {
	return observers.remove(observer);
}

private void notifyElementAdded(E element) {
	for (SetObserver<E> observer : observers)
		observer.added(this, element);
}

์™ธ๊ณ„์ธ ๋ฉ”์„œ๋“œ๊ฐ€ ๋™๊ธฐํ™” ์˜์—ญ ์•ˆ์—์„œ ํ˜ธ์ถœ๋œ๋‹ค๋ฉด ๊ทธ๋™์•ˆ ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ๋Š” ๋ณดํ˜ธ๋œ ์ž์›์„ ์‚ฌ์šฉํ•˜์ง€ ๋ชปํ•˜๊ณ  ๋Œ€๊ธฐํ•˜๊ฒŒ ๋œ๋‹ค. ๋”ฐ๋ผ์„œ ๊ธฐ๋ณธ ๊ทœ์น™์€ ๋™๊ธฐํ™” ์˜์—ญ์—์„œ๋Š” ๊ฐ€๋Šฅํ•œ ์ผ์„ ์ ๊ฒŒ ํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

์ž๋ฐ”์˜ ๋™๊ธฐํ™” ๋น„์šฉ์€ ๋น ๋ฅด๊ฒŒ ๋‚ฎ์•„์ ธ ์™”์ง€๋งŒ, ๊ณผ๋„ํ•œ ๋™๊ธฐํ™”๋ฅผ ํ”ผํ•˜๋Š” ์ผ์€ ์˜คํžˆ๋ ค ๊ณผ๊ฑฐ ์–ด๋Š ๋•Œ๋ณด๋‹ค ์ค‘์š”ํ•˜๋‹ค. ๊ณผ๋„ํ•œ ๋™๊ธฐํ™”๋Š” ๋ณ‘๋ ฌ ์‹คํ–‰ ๊ธฐํšŒ๋ฅผ ์žƒ๊ฒŒ ๋งŒ๋“ค๊ณ , ๋ชจ๋“  ์ฝ”์–ด๊ฐ€ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์ผ๊ด€๋˜๊ฒŒ ๋ณด๊ธฐ ์œ„ํ•ด ์ง€์—ฐ์‹œ๊ฐ„์— ๋Œ€ํ•œ ๋น„์šฉ์ด ํฌ๋‹ค.

๋”ฐ๋ผ์„œ ๊ฐ€๋ณ€ ํด๋ž˜์Šค๋ฅผ ์ž‘์„ฑํ•˜๋ ค๊ฑฐ๋“  ๋‹ค์Œ ๋‘ ์„ ํƒ์ง€ ์ค‘ ํ•˜๋‚˜๋ฅผ ๋”ฐ๋ฅด์ž.

  1. ๋™๊ธฐํ™”๋ฅผ ์ „ํ˜€ ํ•˜์ง€ ๋ง๊ณ , ๊ทธ ํด๋ž˜์Šค๋ฅผ ๋™์‹œ์— ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋Š” ํด๋ž˜์Šค๊ฐ€ ์™ธ๋ถ€์—์„œ ์•Œ์•„์„œ ๋™๊ธฐํ™”ํ•˜๊ฒŒ ํ•˜์ž.
  2. ๋™๊ธฐํ™”๋ฅผ ๋‚ด๋ถ€์—์„œ ์ˆ˜ํ–‰ํ•ด ์Šค๋ ˆ๋“œ ์•ˆ์ „ํ•œ ํด๋ž˜์Šค๋กœ ๋งŒ๋“ค์ž(์•„์ดํ…œ 82).

์ •๋ฆฌ

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

โš ๏ธ **GitHub.com Fallback** โš ๏ธ