item 82 JungHyunLyoo - JAVA-JIKIMI/EFFECTIVE-JAVA3 GitHub Wiki
μ΄λ€ λ©μλκ° λ©ν°μ€λ λ νκ²½μμ μ΄λ»κ² λμν μ§ API λ¬Έμμ κΈ°λ‘ν΄λλ κ²μ λ§€μ° μ€μνλ€.
κΈ°λ‘ν΄λμ§ μμΌλ©΄ ν΄λΌμ΄μΈνΈλ **λκΈ°ν λ κ²½μ°μ κ·Έλ μ§ μμ κ²½μ° 2κ°μ§ μ€ νλλ₯Ό
κ°μ ν΄μ μ½λ©**ν΄μΌνκ³ , μ΄λ μ¬κ°ν μ€λ₯λ‘ μ΄μ΄μ§ μ μλ€. (item78, item79)
λ©μλ μ μΈμ synchronized νμ μλ₯Ό μ μΈνλ κ²μ ꡬν μ΄μμΌ λΏ APIμ
μν₯μ μ£Όμ§ μλλ€. λ°λΌμ API λ¬Έμ λ΄ λ©μλμ synchronized νμ μκ° λΆμμ μ§λΌλ
μ€λ λ μμ μ 100% 보μ₯ν μ μλ€. λ©ν°μ€λ λ νκ²½μμλ APIλ₯Ό μ€λ λ μμ νκ² μ¬μ©νλ €λ©΄
ν΄λμ€κ° μ§μνλ μ€λ λ μμ μ± μμ€μ API λ¬Έμμ μ νν λͺ μν΄μΌ νλ€.
λ€μμ μ€λ λ μμ μ± μμ€μ λ¨κ³μ΄λ€
- λΆλ³(immutable)
μ΄ ν΄λμ€μ μΈμ€ν΄μ€λ λ§μΉ μμμ κ°μμ μΈλΆ λκΈ°νλ νμμλ€ (String, Long, BigInteger(item7))
- 무쑰건μ μ€λ λ μμ (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;
}- μ‘°κ±΄λΆ μ€λ λ μμ (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
}
}
}- μ€λ λ μμ νμ§ μμ
μ΄ ν΄λμ€μ μΈμ€ν΄μ€λ μμ λ μ μλ€.
λ©μλ νΈμΆμ ν΄λΌμ΄μΈνΈκ° μ νν μΈλΆ λκΈ°ν 맀컀λμ¦μΌλ‘ κ°μΈμΌ νλ€ (ArrayList, HashMap)
- μ€λ λ μ λμ
μ΄ ν΄λμ€λ λͺ¨λ λ©μλ νΈμΆμ μΈλΆ λκΈ°νλ‘ κ°μΈλλΌλ λ©ν°μ€λ λ νκ²½μμ μμ νμ§ μλ€.
μ΄ μμ€μ ν΄λμ€λ μΌλ°μ μΌλ‘ μ μ λ°μ΄ν°λ₯Ό μ무 λκΈ°ν μμ΄ μμ νλ€.
λ³΄ν΅ κ³ μλ‘ λ§λ€μ΄μ§μ§λ μκ³ μ°μ°ν λ§λ€μ΄μ§λ κ²½μ°κ° λ§λ€.
μ΄ ν΄λμ€λ€μ μμ λμ΄ μ¬λ°°ν¬λκ±°λ deprecated APIλ‘ μ§μ λλ€ (item78)
μ΄λ€ μμλ‘ νΈμΆν λ μΈλΆ λκΈ°νκ° νμνμ§, κ·Έλ¦¬κ³ κ·Έ μμλ‘ νΈμΆνλ €λ©΄
μ΄λ€ λ½ νΉμ λ½λ€μ μ»μ΄μΌ νλμ§ μλ €μ€μΌ νλ€. μΌλ°μ μΌλ‘ μΈμ€ν΄μ€ μ체λ₯Ό
λ½μΌλ‘ μ»μ§λ§ μμΈλ μλ€. 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μ μ‘°μΈμ λ°λΌ λ½ κ°μ²΄λ₯Ό λκΈ°ν λμ κ°μ²΄ μμΌλ‘ μΊ‘μνν κ²μ΄λ€.)
λΉκ³΅κ° λ½ κ°μ²΄ κ΄μ©κ΅¬λ 무쑰건μ μ€λ λ μμ ν΄λμ€μμλ§ μ¬μ©ν μ μλ€.
μ‘°κ±΄λΆ μ€λ λ μμ ν΄λμ€μμλ νΉμ νΈμΆ μμμ νμν λ½μ΄ 무μμΈμ§λ₯Ό ν΄λΌμ΄μΈνΈμκ²
μλ €μ€μΌ νλ―λ‘ μ΄ κ΄μ©κ΅¬λ₯Ό μ¬μ©ν μ μλ€.