item 82 JungHyunLyoo - JAVA-JIKIMI/EFFECTIVE-JAVA3 GitHub Wiki

λ©€ν‹°μŠ€λ ˆλ“œ κ΄€λ ¨ API λ¬Έμ„œν™”μ˜ μ€‘μš”μ„±

μ–΄λ–€ λ©”μ„œλ“œκ°€ λ©€ν‹°μŠ€λ ˆλ“œ ν™˜κ²½μ—μ„œ μ–΄λ–»κ²Œ λ™μž‘ν•  μ§€ API λ¬Έμ„œμ— κΈ°λ‘ν•΄λ†“λŠ” 것은 맀우 μ€‘μš”ν•˜λ‹€.

기둝해놓지 μ•ŠμœΌλ©΄ ν΄λΌμ΄μ–ΈνŠΈλŠ” **동기화 된 κ²½μš°μ™€ κ·Έλ ‡μ§€ μ•Šμ€ 경우 2κ°€μ§€ 쀑 ν•˜λ‚˜λ₯Ό

κ°€μ •ν•΄μ„œ μ½”λ”©**ν•΄μ•Όν•˜κ³ , μ΄λŠ” μ‹¬κ°ν•œ 였λ₯˜λ‘œ μ΄μ–΄μ§ˆ 수 μžˆλ‹€. (item78, item79)

API λ¬Έμ„œ λ‚΄μ˜ synchronizedλ§ŒμœΌλ‘œλŠ” μ•ˆμ‹¬ν•  수 μ—†λ‹€

λ©”μ„œλ“œ 선언에 synchronized ν•œμ •μžλ₯Ό μ„ μ–Έν•˜λŠ” 것은 κ΅¬ν˜„ 이슈일 뿐 API에

영ν–₯을 μ£Όμ§€ μ•ŠλŠ”λ‹€. λ”°λΌμ„œ API λ¬Έμ„œ λ‚΄ λ©”μ„œλ“œμ— synchronized ν•œμ •μžκ°€ λΆ™μ—ˆμ„ 지라도

μŠ€λ ˆλ“œ μ•ˆμ „μ„ 100% 보μž₯ν•  순 μ—†λ‹€. λ©€ν‹°μŠ€λ ˆλ“œ ν™˜κ²½μ—μ„œλ„ APIλ₯Ό μŠ€λ ˆλ“œ μ•ˆμ „ν•˜κ²Œ μ‚¬μš©ν•˜λ €λ©΄

ν΄λž˜μŠ€κ°€ μ§€μ›ν•˜λŠ” μŠ€λ ˆλ“œ μ•ˆμ •μ„± μˆ˜μ€€μ„ API λ¬Έμ„œμ— μ •ν™•νžˆ λͺ…μ‹œν•΄μ•Ό ν•œλ‹€.

λ‹€μŒμ€ μŠ€λ ˆλ“œ μ•ˆμ „μ„± μˆ˜μ€€μ˜ 단계이닀

  1. λΆˆλ³€(immutable)

이 클래슀의 μΈμŠ€ν„΄μŠ€λŠ” 마치 μƒμˆ˜μ™€ κ°™μ•„μ„œ μ™ΈλΆ€ 동기화도 ν•„μš”μ—†λ‹€ (String, Long, BigInteger(item7))

  1. 무쑰건적 μŠ€λ ˆλ“œ μ•ˆμ „(unconditionally thread-safe)

이 클래슀의 μΈμŠ€ν„΄μŠ€λŠ” μˆ˜μ •λ  수 μžˆλ‹€.

ν•˜μ§€λ§Œ λ‚΄λΆ€μ—μ„œ μΆ©μ‹€νžˆ λ™κΈ°ν™”λ˜μ–΄μžˆλ‹€. (AtomicLong, ConcurrentHashMap)

//AtomicLong.incrementAndGet
public final long incrementAndGet() {
    return unsafe.getAndAddLong(this, valueOffset, 1L) + 1L;
}
//unsafe.getAndAddLong
public final long getAndAddLong(Object var1, long var2, long var4) {
    long var6;
    do {
        var6 = this.getLongVolatile(var1, var2);
    } while(!this.compareAndSwapLong(var1, var2, var6, var6 + var4));

    return var6;
}
//ConcurrentHashMap.putVal
final V putVal(K key, V value, boolean onlyIfAbsent) {
    if (key == null || value == null) throw new NullPointerException();
    int hash = spread(key.hashCode());
    int binCount = 0;
    for (Node<K,V>[] tab = table;;) {
        Node<K,V> f; int n, i, fh;
        if (tab == null || (n = tab.length) == 0)
            tab = initTable();
        else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
            if (casTabAt(tab, i, null,
                    new Node<K,V>(hash, key, value, null)))
                break;                   // no lock when adding to empty bin
        }
        else if ((fh = f.hash) == MOVED)
            tab = helpTransfer(tab, f);
        else {
            V oldVal = null;
            synchronized (f) {
                if (tabAt(tab, i) == f) {
                    if (fh >= 0) {
                        binCount = 1;
                        for (Node<K,V> e = f;; ++binCount) {
                            K ek;
                            if (e.hash == hash &&
                                    ((ek = e.key) == key ||
                                            (ek != null && key.equals(ek)))) {
                                oldVal = e.val;
                                if (!onlyIfAbsent)
                                    e.val = value;
                                break;
                            }
                            Node<K,V> pred = e;
                            if ((e = e.next) == null) {
                                pred.next = new Node<K,V>(hash, key,
                                        value, null);
                                break;
                            }
                        }
                    }
                    else if (f instanceof TreeBin) {
                        Node<K,V> p;
                        binCount = 2;
                        if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,
                                value)) != null) {
                            oldVal = p.val;
                            if (!onlyIfAbsent)
                                p.val = value;
                        }
                    }
                }
            }
            if (binCount != 0) {
                if (binCount >= TREEIFY_THRESHOLD)
                    treeifyBin(tab, i);
                if (oldVal != null)
                    return oldVal;
                break;
            }
        }
    }
    addCount(1L, binCount);
    return null;
}
  1. 쑰건뢀 μŠ€λ ˆλ“œ μ•ˆμ „(conditionally thread-safe)

'무쑰건적 μŠ€λ ˆλ“œ μ•ˆμ „'κ³Ό κ°™μœΌλ‚˜, 일뢀 λ©”μ„œλ“œλŠ” λ™μ‹œμ— μ‚¬μš©ν•˜λ €λ©΄ μ™ΈλΆ€ 동기화가 ν•„μš”ν•˜λ‹€.

Collections.synchronized 래퍼 ν΄λž˜μŠ€κ°€ λ°˜ν™˜ν•œ μ»¬λ ‰μ…˜λ“€μ˜ λ°˜λ³΅μžλ“€μ΄ 이에 ν•΄λ‹Ήν•œλ‹€

public class Collections {
    static class SynchronizedList<E>
            extends java.util.Collections.SynchronizedCollection<E>
            implements List<E> {

        public ListIterator<E> listIterator() {
            return list.listIterator(); // Must be manually synched by user
        }
    }
}
  1. μŠ€λ ˆλ“œ μ•ˆμ „ν•˜μ§€ μ•ŠμŒ

이 클래슀의 μΈμŠ€ν„΄μŠ€λŠ” μˆ˜μ •λ  수 μžˆλ‹€.

λ©”μ„œλ“œ ν˜ΈμΆœμ„ ν΄λΌμ΄μ–ΈνŠΈκ°€ μ„ νƒν•œ μ™ΈλΆ€ 동기화 λ§€μ»€λ‹ˆμ¦˜μœΌλ‘œ 감싸야 ν•œλ‹€ (ArrayList, HashMap)

  1. μŠ€λ ˆλ“œ μ λŒ€μ 

이 ν΄λž˜μŠ€λŠ” λͺ¨λ“  λ©”μ„œλ“œ ν˜ΈμΆœμ„ μ™ΈλΆ€ λ™κΈ°ν™”λ‘œ 감싸더라도 λ©€ν‹°μŠ€λ ˆλ“œ ν™˜κ²½μ—μ„œ μ•ˆμ „ν•˜μ§€ μ•Šλ‹€.

이 μˆ˜μ€€μ˜ ν΄λž˜μŠ€λŠ” 일반적으둜 정적 데이터λ₯Ό 아무 동기화 없이 μˆ˜μ •ν•œλ‹€.

보톡 고의둜 λ§Œλ“€μ–΄μ§€μ§€λŠ” μ•Šκ³  μš°μ—°νžˆ λ§Œλ“€μ–΄μ§€λŠ” κ²½μš°κ°€ λ§Žλ‹€.

이 ν΄λž˜μŠ€λ“€μ€ μˆ˜μ •λ˜μ–΄ μž¬λ°°ν¬λ˜κ±°λ‚˜ deprecated API둜 μ§€μ •λœλ‹€ (item78)

쑰건뢀 μŠ€λ ˆλ“œ μ•ˆμ „ν•œ 클래슀λ₯Ό API λ¬Έμ„œν™”ν•  λ•Œ μ£Όμ˜ν•  점

μ–΄λ–€ μˆœμ„œλ‘œ ν˜ΈμΆœν•  λ•Œ μ™ΈλΆ€ 동기화가 ν•„μš”ν•œμ§€, 그리고 κ·Έ μˆœμ„œλ‘œ ν˜ΈμΆœν•˜λ €λ©΄

μ–΄λ–€ 락 ν˜Ήμ€ 락듀을 μ–»μ–΄μ•Ό ν•˜λŠ”μ§€ μ•Œλ €μ€˜μ•Ό ν•œλ‹€. 일반적으둜 μΈμŠ€ν„΄μŠ€ 자체λ₯Ό

락으둜 μ–»μ§€λ§Œ μ˜ˆμ™Έλ„ μžˆλ‹€. Collections.synchronizedMap의 API λ¬Έμ„œμ—λŠ” λ‹€μŒκ³Ό 같이 써 μžˆλ‹€.

/**
 * Returns a synchronized (thread-safe) map backed by the specified
 * map.  In order to guarantee serial access, it is critical that
 * <strong>all</strong> access to the backing map is accomplished
 * through the returned map.<p>
 *
 * It is imperative that the user manually synchronize on the returned
 * map when iterating over any of its collection views:
 * <pre>
 *  Map m = Collections.synchronizedMap(new HashMap());
 *      ...
 *  Set s = m.keySet();  // Needn't be in synchronized block
 *      ...
 *  synchronized (m) {  // Synchronizing on m, not s!
 *      Iterator i = s.iterator(); // Must be in synchronized block
 *      while (i.hasNext())
 *          foo(i.next());
 *  }
 * </pre>
 * Failure to follow this advice may result in non-deterministic behavior.
 *
 * <p>The returned map will be serializable if the specified map is
 * serializable.
 *
 * @param <K> the class of the map keys
 * @param <V> the class of the map values
 * @param  m the map to be "wrapped" in a synchronized map.
 * @return a synchronized view of the specified map.
 */
public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m) {
    return new SynchronizedMap<>(m);
}

클래슀의 μŠ€λ ˆλ“œ μ•ˆμ „μ„±μ€ 보톡 클래슀의 λ¬Έμ„œν™” 주석에 κΈ°μž¬ν•˜μ§€λ§Œ, λ…νŠΉν•œ νŠΉμ„±μ˜ λ©”μ„œλ“œλΌλ©΄

ν•΄λ‹Ή λ©”μ„œλ“œμ˜ 주석에 κΈ°μž¬ν•˜λ„λ‘ ν•˜μž.

λ˜ν•œ λ°˜ν™˜ νƒ€μž…λ§ŒμœΌλ‘œλŠ” λͺ…ν™•νžˆ μ•Œ 수 μ—†λŠ” 정적 νŒ©ν„°λ¦¬λΌλ©΄ μžμ‹ μ΄ λ°˜ν™˜ν•˜λŠ” 객체의

μŠ€λ ˆλ“œ μ•ˆμ „μ„±μ„ λ°˜λ“œμ‹œ λ¬Έμ„œν™”ν•΄μ•Ό ν•œλ‹€.

μ•žμ„œμ˜ Collectsions.synchronizedMap이 쒋은 μ˜ˆλ‹€.

λΉ„κ³΅κ°œ 락 객체 κ΄€μš©κ΅¬

ν΄λž˜μŠ€κ°€ μ™ΈλΆ€μ—μ„œ μ‚¬μš©ν•  수 μžˆλŠ” 락을 μ œκ³΅ν•˜λ©΄ ν΄λΌμ΄μ–ΈνŠΈμ—μ„œ 일련의 λ©”μ„œλ“œ ν˜ΈμΆœμ„

μ›μžμ μœΌλ‘œ μˆ˜ν–‰ν•  수 μžˆλ‹€. ν•˜μ§€λ§Œ 이 μœ μ—°μ„±μ—λŠ” λŒ€κ°€κ°€ λ”°λ₯Έλ‹€.

λ‚΄λΆ€μ—μ„œ μ²˜λ¦¬ν•˜λŠ” κ³ μ„±λŠ₯ λ™μ‹œμ„± μ œμ–΄ λ©”μ»€λ‹ˆμ¦˜κ³Ό ν˜Όμš©ν•  수 μ—†κ²Œ λœλ‹€.

κ·Έλž˜μ„œ ConcurrentHashMap같은 λ™μ‹œμ„± μ»¬λ ‰μ…˜κ³ΌλŠ” ν•¨κ»˜ μ‚¬μš©ν•˜μ§€ λͺ»ν•œλ‹€.

λ˜ν•œ, ν΄λΌμ΄μ–ΈνŠΈκ°€ 곡개된 락을 였래 μ₯κ³  놓지 μ•ŠλŠ”

μ„œλΉ„μŠ€ κ±°λΆ€ 곡격(denial-of-service attack)을 μˆ˜ν–‰ν•  μˆ˜λ„ μžˆλ‹€.

이 μ„œλΉ„μŠ€ κ±°λΆ€ 곡격을 λ§‰μœΌλ €λ©΄ synchronized λ©”μ„œλ“œ λŒ€μ‹  λΉ„κ³΅κ°œ 락 객체λ₯Ό μ‚¬μš©ν•΄μ•Ό ν•œλ‹€.

//final을 톡해 락 객체의 ꡐ체 λ°©μ§€
private final Object lock = new Object();

public void foo() {
    synchronized (lock) {

    }
}

λΉ„κ³΅κ°œ 락 κ°μ²΄λŠ” 클래슀 λ°”κΉ₯에선 볼수 μ—†μœΌλ‹ˆ, ν΄λΌμ΄μ–ΈνŠΈκ°€ κ·Έ 객체의 동기화에 κ΄€μ—¬ν•  수 μ—†λ‹€.

(item15의 쑰언을 따라 락 객체λ₯Ό 동기화 λŒ€μƒ 객체 μ•ˆμœΌλ‘œ μΊ‘μŠν™”ν•œ 것이닀.)

λΉ„κ³΅κ°œ 락 객체 κ΄€μš©κ΅¬λŠ” 무쑰건적 μŠ€λ ˆλ“œ μ•ˆμ „ ν΄λž˜μŠ€μ—μ„œλ§Œ μ‚¬μš©ν•  수 μžˆλ‹€.

쑰건뢀 μŠ€λ ˆλ“œ μ•ˆμ „ ν΄λž˜μŠ€μ—μ„œλŠ” νŠΉμ • 호좜 μˆœμ„œμ— ν•„μš”ν•œ 락이 무엇인지λ₯Ό ν΄λΌμ΄μ–ΈνŠΈμ—κ²Œ

μ•Œλ €μ€˜μ•Ό ν•˜λ―€λ‘œ 이 κ΄€μš©κ΅¬λ₯Ό μ‚¬μš©ν•  수 μ—†λ‹€.

⚠️ **GitHub.com Fallback** ⚠️