Item 79. Excessive Synchronization
๊ณผ๋ํ ๋๊ธฐํ๋ ํผํ๋ผ
๋๊ธฐํ๋ ์ค๋ ๋ ์์ ์ฑ์ ๋ณด์ฅํ๊ธฐ ์ํด ์ฌ์ฉ๋๋๋ฐ, ๊ณผ๋ํ ๋๊ธฐํ๋ ์ฑ๋ฅ์ ์ ํ์ํค๊ฑฐ๋, ๋ฐ๋๋ฝ์ ๋ฐ์์ํค๊ณ , ๋ ํฐ ๋ฌธ์ ๋ฅผ ๋ฐ์์ํฌ ์ ์๋ค. ์ด๋ฌํ ๋ฌธ์ ๋ฅผ ํผํ๊ธฐ ์ํด์ , ๋๊ธฐํ ๋ฉ์๋๋ ๋๊ธฐํ ๋ธ๋ก ์์ ์ ์ด๋ฅผ ํด๋ผ์ด์ธํธ์๊ฒ ๋๊ธฐ๋ ์๋์ ๊ฐ์ ์ฌ์ฉ์ ํผํด์ผ ํ๋ค.
๋๊ธฐํ๋ ์์ญ ์์์ ์ฌ์ ์ํ ์ ์๋ ๋ฉ์๋ ํธ์ถ
ํด๋ผ์ด์ธํธ๊ฐ ๋๊ฒจ์ค ํจ์ ๊ฐ์ฒด ํธ์ถ
์ธ๋ถ์์ ์๋ฌ ๋ฐ์ ์ฝ๋๋ ๋๊ธฐํ ๋ ์์ญ ๋ด์์ ์์ธ๋ฅผ ๋ฐ์์ํค๊ฑฐ๋, ๋ฐ๋๋ฝ์ ๋น ์ง๊ฑฐ๋ ๋ฐ์ดํฐ๋ฅผ ์์์ํฌ ์ ์๋ค.
์์ธ๊ฐ ๋ฐ์ํ๋ ์์
์๋๋ ์งํฉ(Set)์ ๊ฐ์ผ ๋ํผ ํด๋์ค๋ก, ์งํฉ์ ์์๊ฐ ์ถ๊ฐ๋๋ฉด ์๋ฆผ์ ๋ฐ์ ์ ์๋ ๊ด์ฐฐ์ ํจํด์ ๊ตฌํํ ์ฝ๋์ด๋ค.
public class ObservableSet<E> extends ForwardingSet<E> {
private final List<SetObserver<E>> observers = new ArrayList<>();
public ObservableSet(Set<E> set) {
super(set);
}
// ๊ตฌ๋
์ถ๊ฐ
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);
}
return result;
}
}
public class Test {
public static void main(String[] args) {
ObservableSet<Integer> set = new ObservableSet<>(new HashSet<>());
// ๊ด์ฐฐ์ ์ถ๊ฐ
set.addObserver(new SetObserver<>() {
@Override
public void added(ObservableSet<Integer> set, Integer element) {
System.out.println(element);
// ํน์ ์์(=23)์ด ์ถ๊ฐ๋๋ฉด ์๊ธฐ ์์ (๊ด์ฐฐ์)์ ์ ๊ฑฐ
if (element == 23) {
set.removeObserver(this);
}
}
});
for (int i = 0; i < 100; i++) {
set.add(i);
}
}
}
์ ์ฝ๋๋ SetObserver๋ฅผ ๊ตฌํํ ๊ด์ฐฐ์๋ฅผ ์ถ๊ฐํ๊ณ , ์์๊ฐ ์ถ๊ฐ๋ ๋๋ง๋ค ๊ด์ฐฐ์์๊ฒ ์๋ฆผ์ ๋ณด๋ด๋ ObservableSet ํด๋์ค์ด๋ค.
์ฌ๊ธฐ์ ํน์ ์์์์ ๊ด์ฐฐ์ ์ค์ค๋ก๋ฅผ ์ ๊ฑฐํ๋ ์ฝ๋๋ฅผ ์ถ๊ฐํ๋๋ฐ, ์ด ์ฝ๋์์ ConcurrentModificationException
์๋ฌ๊ฐ ๋ฐ์ํ๋ค.
set.add(i)
๋ฉ์๋ ๋ด๋ถ์์notifyElementAdded
๋ฅผ ํธ์ถํ๊ณ , ํด๋น ๋ฉ์๋ ๋ด๋ถ์์added
๋ฉ์๋๋ฅผ ํธ์ถํ๊ฒ ๋จ์ฆ,
added
๋ฉ์๋๋notifyElementAdded
์์ ๋ฆฌ์คํธ๋ฅผ ์ํํ๋ ๋์ค์ ์ํ๋จ๊ตฌํํ
added
๋ฉ์๋ ๋ด๋ถ์์removeObserver
๋ฅผ ํธ์ถํ์ฌ ๋ฆฌ์คํธ๋ฅผ ์์ ํ๋ ค ์๋ํ์ง๋ง
notifyElementAdded
๋ฉ์๋ ๋ด๋ถ์์ ๋ฆฌ์คํธ ์ํ ๋์ค ๋ฆฌ์คํธ๋ฅผ ์์ ํ๋ ค ์๋ํ๊ธฐ ๋๋ฌธ์ ์๋ฌ ๋ฐ์
์ ์ฝ๋์์ notifyElementAdded
๋๊ธฐํ ๋ธ๋ก ์์ ๋ด๋ถ์์ ์ํํ๋ ์ฝ๋๊ฐ ์์ด ๋์ ์์ ์ด ์ผ์ด๋์ง ์๋๋ก ๋ณด์ฅํ์ง๋ง, ์ฝ๋ฐฑ์ ๊ฑฐ์ณ ์์ ํ๋ ๋ถ๋ถ์ ๋๊ธฐํ ๋ธ๋ก ๋ฐ ์์ญ์ด๊ธฐ ๋๋ฌธ์ ๋ง์ง ๋ชปํ๋ค.
๋ฐ๋๋ฝ์ด ๋ฐ์ํ๋ ์์
๋ค์์ ํ ์คํธ ์ํ ๋ถ๋ถ์ ์์ ํ์ฌ, ๋ค๋ฅธ ์ค๋ ๋์์ ๊ด์ฐฐ์๋ฅผ ์ ๊ฑฐํ๋๋ก ์์ ํด๋ณด์.
public class Test {
public static void main(String[] args) {
ObservableSet<Integer> set = new ObservableSet<>(new HashSet<>());
// ๊ด์ฐฐ์ ์ถ๊ฐ
set.addObserver(new SetObserver<>() {
@Override
public void added(ObservableSet<Integer> set, Integer element) {
System.out.println(element);
if (element == 23) {
// removeObserver๋ฅผ ์ง์ ํธ์ถํ์ง ์๊ณ ๋ค๋ฅธ ์ค๋ ๋์์ ์ํ
ExecutorService exec = Executors.newSingleThreadExecutor();
try {
exec.submit(() -> set.removeObserver(this)).get();
} catch (InterruptedException | ExecutionException e) {
throw new AssertionError(e);
} finally {
exec.shutdown();
}
}
}
});
for (int i = 0; i < 100; i++) {
set.add(i);
}
}
}
์ด๋ฒ ์ฝ๋์์ ์์ธ๊ฐ ๋ฐ์ํ์ง ์์ง๋ง ๋ฐ๋๋ฝ์ด ๋ฐ์ํ์ฌ ๊ณ์ ๋๊ธฐํ๋ ์ํ๊ฐ ๋๋ค.
๋ค๋ฅธ ์ค๋ ๋์์
set.removeObserver(this)
๋ฅผ ํธ์ถํ๋ฉด์ ์ ๊ธ์ ํ๋ํ๋ ค ์๋ํ์ง๋ง ์ด๋ฏธ ์ํ ์ค์ธ ๋ฉ์ธ ์ค๋ ๋๊ฐ
notifyElementAdded
๋ฉ์๋ ๋ด๋ถ์์ ์ ๊ธ์ ํ๋ํ๊ณ ์์ด ๋๊ธฐ๋ํ ๋ฉ์ธ ์ค๋ ๋๋ ๋ค๋ฅธ ์ค๋ ๋์์
set.removeObserver(this)
๋ฅผ ๋๊ธฐํ๊ณ ์์ด ๋ฐ๋๋ฝ ๋ฐ์
ํด๊ฒฐ ๋ฐฉ๋ฒ
์ด๋ฌํ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด์ , ๋๊ธฐํ๋ ์์ญ ์์์ ํด๋ผ์ด์ธํธ๊ฐ ๋๊ฒจ์ค ํจ์ ๊ฐ์ฒด ํธ์ถ
์ ํผํ๋ฉด ๋๋ค.
private void notifyElementAdded(E element) {
List<SetObserver<E>> snapshot = null;
synchronized (observers) {
snapshot = new ArrayList<>(observers);
}
for (SetObserver<E> observer : snapshot) {
// ๋๊ธฐํ ๋ธ๋ก ๋ฐ์์ ์ธ๋ถ ์ ์ ๋ `added` ๋ฉ์๋ ํธ์ถ
observer.added(this, element);
}
}
์ด๋ ๊ฒ ์ง์ ๋ก์ง์ ์ผ๋ถ ์์ ํ์ฌ ํด๊ฒฐํ๋ ๋ฐฉ๋ฒ๋ ์กด์ฌํ์ง๋ง, ์๋ฐ์์ ์ ๊ณตํ๋ ๋์์ฑ ์ปฌ๋ ์ ์ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ๋ ์๋ค.
private final Set<SetObserver<E>> observers = new CopyOnWriteArraySet<>();
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);
}
// }
}
ArrayList
๋์ CopyOnWriteArraySet
๋ก ์์ ํ๊ธฐ๋ง ํ๋ฉด ๊ธฐ์กด ์ฝ๋์์๋ ๋์ ์์ ์ด ์ผ์ด๋๋ ์์ ํ๊ฒ ๋์ํ๋ค.
๋ํ ์ปฌ๋ ์
๋ด๋ถ์์ ๋๊ธฐํ๋ฅผ ์ฒ๋ฆฌํ๊ธฐ ๋๋ฌธ์ ๋ช
์์ ์ผ๋ก ์ถ๊ฐํ ๋๊ธฐํ ๋ธ๋ก์ ์ ๊ฑฐํด๋ ๋๋ค.
๊ฒฐ๋ก
๋จ์ํ ์์ธ ๋ฐ์์ด๋ ๋ฐ๋๋ฝ์ ํผํ๋ ๊ฒ๋ ์ค์ํ์ง๋ง, ๋ฉํฐ ์ค๋ ๋ ์ฒ๋ฆฌ์ ๋ชฉ์ ์ ์ฑ๋ฅ ํฅ์์ด๋ค. ๋ฉํฐ ์ค๋ ๋ ์ํ ์ค ๊ฐ์ฅ ๋ง์ ๋น์ฉ์ด ๋ฐ์ํ๋ ๋ถ๋ถ์ ์ง์ฐ ์๊ฐ์ผ๋ก, ์ง์ฐ ์๊ฐ์ ์ค์ด๋ ๊ฒ์ ๊ณง ์ฑ๋ฅ ํฅ์์ผ๋ก ์ด์ด์ง๋ค. ๋๊ธฐํ๋ฅผ ํจ๊ณผ์ ์ผ๋ก, ๊ทธ๋ฆฌ๊ณ ์์ ์ ์ผ๋ก ์ฌ์ฉํ๊ธฐ ์ํด์ ์๋ ๊ท์น์ ๋ฐ๋ฅด์.
์ฌ์ ์ํ ์ ์๊ฑฐ๋ ์ธ๋ถ์์ ๋์ด์จ ๋ฉ์๋ ๋๊ธฐํ ์์ญ ๋ด์์ ์ํ ๊ธ์ง
๋๊ธฐํ ์์ญ ๋ด์์ ์ํํ๋ ์ผ์ ์ ๊ฒํ์ฌ ๋ฝ์ ๊ฐ์ง๊ณ ์๋ ์๊ฐ์ ์ต์ํ
Last updated
Was this helpful?