item 79 sijun - JAVA-JIKIMI/EFFECTIVE-JAVA3 GitHub Wiki
๊ณผ๋ํ ๋๊ธฐํ๋ ์ฑ๋ฅ์ ๋จ์ด๋จ๋ฆฌ๊ณ , ๊ต์ฐฉ์ํ์ ๋น ๋จ๋ฆฌ๊ณ , ์ฌ์ง์ด ์์ธกํ ์ ์๋ ๋์์ ๋ณ๊ธฐ๋ ํ๋ค.
์๋ต ๋ถ๊ฐ์ ์์ ์คํจ๋ฅผ ํผํ๋ ค๋ฉด ๋๊ธฐํ ๋ฉ์๋๋ ๋๊ธฐํ ๋ธ๋ก ์์์๋ ์ ์ด๋ฅผ ์ ๋๋ก ํด๋ผ์ด์ธํธ์ ์๋ํ๋ฉด ์ ๋๋ค. ์๋ฅผ ๋ค์ด ๋๊ธฐํ๋ ์์ญ ์์์๋ ์ฌ์ ์ํ ์ ์๋ ๋ฉ์๋๋ ํธ์ถํ๋ฉด ์ ๋๋ฉฐ, ํด๋ผ์ด์ธํธ๊ฐ ๋๊ฒจ์ค ํจ์ ๊ฐ์ฒด(์์ดํ 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๋ฅผ ๊ตฌํํ ํด๋์ค๋ก, ๋ด๋ถ๋ฅผ ๋ณ๊ฒฝํ๋ ์์
์ ํญ์ ๊นจ๋ํ ๋ณต์ฌ๋ณธ์ ๋ง๋ค์ด ์ํํ๋๋ก ๊ตฌํํ๋ค.
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);
}
์ธ๊ณ์ธ ๋ฉ์๋๊ฐ ๋๊ธฐํ ์์ญ ์์์ ํธ์ถ๋๋ค๋ฉด ๊ทธ๋์ ๋ค๋ฅธ ์ค๋ ๋๋ ๋ณดํธ๋ ์์์ ์ฌ์ฉํ์ง ๋ชปํ๊ณ ๋๊ธฐํ๊ฒ ๋๋ค. ๋ฐ๋ผ์ ๊ธฐ๋ณธ ๊ท์น์ ๋๊ธฐํ ์์ญ์์๋ ๊ฐ๋ฅํ ์ผ์ ์ ๊ฒ ํ๋ ๊ฒ์ด๋ค.
์๋ฐ์ ๋๊ธฐํ ๋น์ฉ์ ๋น ๋ฅด๊ฒ ๋ฎ์์ ธ ์์ง๋ง, ๊ณผ๋ํ ๋๊ธฐํ๋ฅผ ํผํ๋ ์ผ์ ์คํ๋ ค ๊ณผ๊ฑฐ ์ด๋ ๋๋ณด๋ค ์ค์ํ๋ค. ๊ณผ๋ํ ๋๊ธฐํ๋ ๋ณ๋ ฌ ์คํ ๊ธฐํ๋ฅผ ์๊ฒ ๋ง๋ค๊ณ , ๋ชจ๋ ์ฝ์ด๊ฐ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์ผ๊ด๋๊ฒ ๋ณด๊ธฐ ์ํด ์ง์ฐ์๊ฐ์ ๋ํ ๋น์ฉ์ด ํฌ๋ค.
๋ฐ๋ผ์ ๊ฐ๋ณ ํด๋์ค๋ฅผ ์์ฑํ๋ ค๊ฑฐ๋ ๋ค์ ๋ ์ ํ์ง ์ค ํ๋๋ฅผ ๋ฐ๋ฅด์.
- ๋๊ธฐํ๋ฅผ ์ ํ ํ์ง ๋ง๊ณ , ๊ทธ ํด๋์ค๋ฅผ ๋์์ ์ฌ์ฉํด์ผ ํ๋ ํด๋์ค๊ฐ ์ธ๋ถ์์ ์์์ ๋๊ธฐํํ๊ฒ ํ์.
- ๋๊ธฐํ๋ฅผ ๋ด๋ถ์์ ์ํํด ์ค๋ ๋ ์์ ํ ํด๋์ค๋ก ๋ง๋ค์(์์ดํ 82).
๊ต์ฐฉ์ํ์ ๋ฐ์ดํฐ ํผ์์ ํผํ๋ ค๋ฉด ๋๊ธฐํ ์์ญ ์์์ ์ธ๊ณ์ธ ๋ฉ์๋๋ฅผ ์ ๋ ํธ์ถํ์ง ๋ง์. ์ผ๋ฐํํด ์ด์ผ๊ธฐํ๋ฉด, ๋๊ธฐํ ์์ญ ์์์์ ์์ ์ ์ต์ํ์ผ๋ก ์ค์ด์. ๊ฐ๋ณ ํด๋์ค๋ฅผ ์ค๊ณํ ๋๋ ์ค์ค๋ก ๋๊ธฐํํด์ผ ํ ์ง ๊ณ ๋ฏผํ์. ๋ฉํฐ์ฝ์ด ์ธ์์ธ ์ง๊ธ์ ๊ณผ๋ํ ๋๊ธฐํ๋ฅผ ํผํ๋ ๊ฒ ๊ณผ๊ฑฐ ์ด๋ ๋๋ณด๋ค ์ค์ํ๋ค. ํฉ๋นํ ์ด์ ๊ฐ ์์ ๋๋ง ๋ด๋ถ์์ ๋๊ธฐํํ๊ณ , ๋๊ธฐํํ๋์ง ์ฌ๋ถ๋ฅผ ๋ฌธ์์ ๋ช ํํ ๋ฐํ์