We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Originally posted by JoisFe April 2, 2023
충분하지 못한 동기화의 피해
public class ForwardingSet<E> implements Set<E> { private final Set<E> s; public ForwardingSet(Set<E> s) { this.s = s; } @Override public int size() { return s.size(); } @Override public boolean isEmpty() { return s.isEmpty(); } @Override public boolean contains(Object o) { return s.contains(o); } @Override public Iterator<E> iterator() { return s.iterator(); } @Override public Object[] toArray() { return s.toArray(); } @Override public <T> T[] toArray(T[] a) { return s.toArray(a); } @Override public boolean add(E e) { return s.add(e); } @Override public boolean remove(Object o) { return s.remove(o); } @Override public boolean containsAll(Collection<?> c) { return s.containsAll(c); } @Override public boolean addAll(Collection<? extends E> c) { return s.addAll(c); } @Override public boolean retainAll(Collection<?> c) { return s.retainAll(c); } @Override public boolean removeAll(Collection<?> c) { return s.removeAll(c); } @Override public void clear() { s.clear(); } @Override public boolean equals(Object o) { return s.equals(o); } @Override public int hashCode() { return s.hashCode(); } @Override public String toString() { return s.toString(); } }
public class ObservableSet<E> extends ForwardingSet<E>{ public ObservableSet(Set<E> s) { super(s); } private final List<SetObserver<E>> observers = new ArrayList<>(); public void addObserver(SetObserver<E> observer) { synchronized (this.observers) { this.observers.add(observer); } } public boolean removeObserver(SetObserver<E> observer) { synchronized (this.observers) { return this.observers.remove(observer); } } public void notifyElementAdded(E element) { synchronized (this.observers) { for (SetObserver<E> observer : observers) { observer.added(this, element); } } } @Override public boolean add(E e) { boolean added = super.add(e); if (added) { notifyElementAdded(e); } return added; } @Override public boolean addAll(Collection<? extends E> c) { boolean result = false; for (E e : c) { result |= add(e); } return result; } }
@FunctionalInterface public interface SetObserver<E> { // ObservableSet에 원소가 더해지면 호출됨 void added(ObservableSet<E> set, E element); }
표준 함수형 인터페이스를 사용하라
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<>() { @Override public void added(ObservableSet<Integer> set, Integer element) { System.out.println(element); if (element == 23) { set.removeObserver(this); } } });
ConcurrentModificationException
람다는 자신을 참조할 수단이 없음
다시 돌아와
set.addObserver(new SetObserver<>() { @Override public void added(ObservableSet<Integer> set, Integer element) { System.out.println(element); if (element == 23) { ExecutorService exec = Executors.newSingleThreadExecutor(); try { exec.submit(() -> set.removeObserver(this)).get(); } catch (ExecutionException | InterruptedException ex) { throw new AssertionError(ex); } finally { exec.shutdown(); } } } });
public void notifyElementAdded(E element) { List<SetObserver<E>> snapshot = null; synchronized (this.observers) { snapshot = new ArrayList<>(this.observers); } for (SetObserver<E> observer : snapshot) { observer.added(this, element); } }
public class ObservableSet<E> extends ForwardingSet<E>{ public ObservableSet(Set<E> s) { super(s); } private final List<SetObserver<E>> observers = new CopyOnWriteArrayList<>(); public void addObserver(SetObserver<E> observer) { this.observers.add(observer); } public boolean removeObserver(SetObserver<E> observer) { return this.observers.remove(observer); } public void notifyElementAdded(E element) { for (SetObserver<E> observer : observers) { observer.added(this, element); } } ~~~
The text was updated successfully, but these errors were encountered:
No branches or pull requests
Discussed in https://github.com/orgs/Study-2-Effective-Java/discussions/191
Originally posted by JoisFe April 2, 2023
아이템 79. 과도한 동기화는 피하라
충분하지 못한 동기화의 피해
에 다뤘는데 이번 주제는 반대 상황을 다룸과도한 동기화의 문제점
응답 불가와 안전 실패를 피하려면 동기화 메서드나 동기화 블록 안에서는 제어를 절대로 클라이언트에 양도하면 안됨!
표준 함수형 인터페이스를 사용하라
에 맞는대로라면 BiConsumer 를 사용하는게 맞았지 않나 생각함....ObservableSet은 눈에 보기에 잘 동작할 것으로 보이나 문제가 있음
ConcurrentModificationException
을 던짐참고
람다는 자신을 참조할 수단이 없음
(아이템 42. 익명 클래스보다는 람다를 사용하라 #106 참고)다시 돌아와
ConcurrentModificationException을 던진 이유
교착 상태에 빠지는 이유
위와 똑같은 상황이지만 불변식이 임시로 깨진 경우
해결 방법
명시적으로 동기화한 곳이 사라졌다는 것이 중요한 포인트!
동기화 규칙
기본 규칙은 동기화 영역에서는 가능한 한 일을 적게 하는 것!
과도한 동기화와 성능
가변 클래스를 작성하는 2가지 선택지
자바도 초장기에는 이 지침을 따르지 않은 클래스가 많았음
선택하기 어렵다면 동기화하지 말고 대신 문서에 "스레드 안전하지 않다" 라고 명시하자!
클래스 내부에서 동기화 하는 경우 다양한 기법
여러 스레드가 호출할 가능성이 있는 메서드가 정적 필드를 수정한다면 그 필드를 사용하기 전에 반드시 동기화해야 함! (비결정적 행동도 용인하는 클래스라면 상관 없음)
정리
The text was updated successfully, but these errors were encountered: