이펙티브 자바
아이템 81. wait 와 notify 보다는 동시성 유틸리티를 애용하라.
wait 와 notify는 올바르게 사용하기가 아주 까다로우니 고수준 동시성 유틸리티를 사용하자.
동시성 컬렉션에서 동시성을 무력화하는 건 불가능하며, 외부에서 락을 추가로 사용하면 오히려 속도가 느려진다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| package com.github.sejoung.codetest.concurrent.utilities;
import java.util.Map; import java.util.concurrent.ConcurrentHashMap;
public class Intern {
private static final Map<String, String> map = new ConcurrentHashMap<>();
public static String intern(String s) { String previousValue = map.putIfAbsent(s, s); return previousValue == null ? s : previousValue; } }
|
위에 코드에서 개선할 포인트가 있는데 putIfAbsent 보다 get이 빠르다 코드를 수정하면
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| package com.github.sejoung.codetest.concurrent.utilities;
import java.util.Map; import java.util.concurrent.ConcurrentHashMap;
public class Intern {
private static final Map<String, String> map = new ConcurrentHashMap<>();
public static String intern(String s) { String result = map.get(s); if (result == null) { result = map.putIfAbsent(s, s); if (result == null) { result = s; } } return result; } }
|
위에처럼 필요할때만 putIfAbsent를 호출하는것으로 바꾸는것이 좋다.
Collections.synchronizedMap()
보다 ConcurrentHashMap
를 사용하는것이 훨씬 좋다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| package com.github.sejoung.codetest.concurrent.utilities;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors;
public class ConcurrentTimer {
private ConcurrentTimer() { }
public static long time(Executor executor, int concurrency, Runnable action) throws InterruptedException { CountDownLatch ready = new CountDownLatch(concurrency); CountDownLatch start = new CountDownLatch(1); CountDownLatch done = new CountDownLatch(concurrency);
for (int i = 0; i < concurrency; i++) { executor.execute(() -> { ready.countDown(); try { start.await(); action.run(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { done.countDown(); } }); }
ready.await(); long startNanos = System.nanoTime(); start.countDown(); done.await(); return System.nanoTime() - startNanos; }
public static void main(String[] args) throws InterruptedException {
int concurrency = 10; ExecutorService es = Executors.newFixedThreadPool(concurrency);
long nanotime = ConcurrentTimer.time(es,concurrency,()-> System.out.println("test"));
System.out.println(nanotime);
es.shutdown(); } }
|
위에서는 CountDownLatch를 사용하는 법이다. 위에 기능을 wait 와 notify를 사용하여 구현하려면 엄청 코드가 지저분해진다.
시간 간격을 잴때는 System.currentTimeMillis()
이것 보다 System.nanoTime()
를 사용하자.
wait메서드를 사용할때의 관용구이다. 반드시 반복문에서 사용하고 반복문 밖에서는 사용하지 말자.
1 2 3 4 5 6 7
| synchronized (obj){ while (조건이 충족되지 않았다){ obj.wait() } }
|
코드를 새로작성한다면 wait 와 notify는 거의 사용할일이 없다.
참조