💡 과도한 동기화는 **성능을 떨어뜨리고**, **교착상태**에 빠뜨리고, 심지어 **예측할 수 없는 동작**을 낳기도 한다.
💡 응답 불가와 안전 실패를 피하려면 동기화 메서드나 동기화 블록 안에서는 제어를 절대로 **클라이언트에 양도하면 안 된다**.
예를 들어 동기화된 영역 안에서는 재정의할 수 있는 메서드
는 호출하면 안 되며, 클라이언트가 넘겨준 함수 객체
를 호출해서도 안 된다.
왜냐하면 그 메서드가 무슨 일을 할지 알지 못하며 통제할 수 없다는 뜻이다.
이는 예외
를 발생 시키거나 교착상태
에 빠지거나 데이터를 훼손할 수도 있다.
아래의 코드는 어떤 집합(Set
)을 감싼 래퍼 클래스이다.
이 클래스의 클라이언트는 집합에 원소가 추가되면 알림을 받을 수 있다.
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
메서드를 호출하여 구독을 신청하거나 해지한다.ObservableSet
은 잘 작동할 것 같다.즉, 아래의 코드는 0부터 99까지 출력한다.