Closed okhee closed 1 year ago
public class ObservableSet<E> extends ForwardingSet<E> { ... private final List<SetObserver<E>> observers = new ArrayList<SetObserver<E>> (); ... 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; } } public interface SetObserver<E> { // 요소가 Set에 추가될 때 호출된다(call back) void added(ObservableSet<E> set, E element); }
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); } }
ConcurrentModificationException
public static void main(String[] args) { ObservableSet<Integer> set = new ObservableSet<>(new HashSet<>()); // 실행 (executor) 서비스를 사용하는 observer(관찰자) set.addObserver(new SetObserver<>() { public void added(ObservableSet<Integer> s, Integer e) { System.out.println(e); if(e == 23) { ExecutorService exec = Executors.newSingleThreadExecutor(); try { // 여기서 lock 발생 (메인 스레드는 작업을 기다리고 있음) // 특정 태스크가 완료되기를 기다림 (submit().get()) (#80 참고) exec.submit(() -> s.removeObserver(this)).get(); } catch(ExecutionException | InterruptedException ex) { throw new AssertionError(ex); } finally { exec.shutdown(); } } } }); for (int i = 0; i < 100; i++) { set.add(i); } }
교착상태
// 동기화된 블록 밖으로 외계인 메소드를 옮긴다 private void notifyElementAdded(E element) { List<SetObserver<E>> snapshot = null; synchronized(observers) { snapshot =new ArrayList<SetObserver<E>> (observers); } for (SetObserver<E> observer : snapshot) observer.added(this, element) ; }
CopyOnWriteArrayList
78 에서 충분하지 못한 동기화의 피해를 다뤘다면, 이번엔 반대 상황
과도한 동기화는
동기화 메서드나 동기화 블록 안에서는 클라이언트에게 제어권을 넘기지 말 것
잘못된 코드(예외 발생) - 동기화 블록 안에서 외계인 메서드 호출
ConcurrentModificationException
발생잘못된 코드(교착 상태) - 쓸데없이 백그라운드 스레드를 사용하는 관찰자
교착상태
에 빠짐예외 및 교착상태 해결법 - 1
예외 및 교착상태 해결법 - 2
CopyOnWriteArrayList
사용성능 측면
그럼 어떻게?
사례
그래서 뭘 쓰라고??
정리