<aside> 🚨 충분하지 못한 동기화도 문제이지만 과도한 동기화도 문제다. 과도한 동기화는 성능을 떨어뜨리고, 교착상태에 빠뜨리고, 심지어 예측할 수 없는 동작을 낳기도 한다.

</aside>

1. 예측할 수 없는 동작을 낳는 과도한 동기화

<aside> 📚 응답 불가안전 실패를 피하려면 동기화 메서드/블록 안에서는 제어권을 클라이언트에게 양도하면 안된다.

</aside>

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); }
    }

		// 외계인 메서드 호출 부분
		// observer의 added : 동기화된 영역에서 재정의할 수 있는 메서드(제어권이 클라이언트에 있다)
    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);
        return result;
    }
}
public class Main {
    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);

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

2. 교착 상태에 빠트리는 과도한 동기화

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.sumbit(() -> s.removeObserver(this)).get();
            } catch (ExecutionException | InterruptedException ex) {
                throw new AssertionError(ex);
            } finally {
                exec.shtdown();
            }
            s.removeObserver(this);
        }
    }
});