JiwonDev

9. μ˜ˆμ™Έ(Exception) 와 λ™μ‹œμ„±(λ©€ν‹°μŠ€λ ˆλ“œ)

by JiwonDev

# μ˜ˆμ™ΈλŠ” μ§„μ§œ μ˜ˆμ™Έμƒν™©μ—μ„œ μ‚¬μš©ν•˜λΌ.

정상적인 μ„œλΉ„μŠ€ 흐름에 μ˜ˆμ™Έλ₯Ό μ‚¬μš©ν•˜μ§€ 마라.

try-catch 문은 JVM의 μ΅œμ ν™”λ₯Ό 막아버리고 흐름 μ œμ–΄μ— 쓰인 μ˜ˆμ™Έ λ•Œλ¬Έμ— μ‹€μ œ 버그가 μˆ¨κ²¨μ Έμ„œ 디버깅이 μ–΄λ €μ›Œμ§„λ‹€.

 

μ΄λŠ” μ‚¬μš©μž(ν΄λΌμ΄μ–ΈνŠΈ)의 μž…μž₯μ—μ„œ μƒκ°ν•˜λ©΄ 쉽닀. μ•„λž˜μ™€ 같은 μ½”λ“œλ₯Ό μƒκ°ν•΄λ³΄μž.

for(Iterator<Foo> i = collection.iterator(); i.hasNext();){
    Foo foo = i.next();
}

Iterator μΈν„°νŽ˜μ΄μŠ€μ˜ next(), hasNext() λ©”μ„œλ“œκ°€ μ—†λ‹€λ©΄ μ•„λž˜μ™€ 같이 μ‚¬μš©ν•΄μ•Ό ν•  것이닀. μ΄λŠ” 쒋지 μ•Šλ‹€.

try{
    Iterator<Foo> i = collection.iterator();
    while(true)
        Foo foo = i.next();
        ...
    
} catch (NoSuchElementException e){}

μ›μ†Œκ°€ μ—†λŠ”κ±΄ 정상적인 둜직 흐름이닀. 였λ₯˜λ‚˜ μ˜ˆμ™Έμƒν™©μ΄ μ•„λ‹ˆλ‹€.

μ˜ˆμ™Έκ°€ λ°œμƒν–ˆμ„ λ•ŒλŠ” 정말 μ˜ˆμ™Έ 상황이 일어났을 λ•Œμ΄λ‹€. μ‚¬μš©μž(ν΄λΌμ΄μ–ΈνŠΈ)μ—κ²Œ ν˜Όλ™μ„ 주지 마라.

 

πŸ“Œ μƒνƒœκ²€μ‚¬ λ©”μ„œλ“œ(.hasNext()처럼 값을 λ°”λ‘œ λ°˜ν™˜ν•˜λŠ” λ©”μ„œλ“œ)와 μƒνƒœ μ˜μ‘΄λ©”μ„œλ“œ(.next와 같이 true, false 체크 μš©λ„)

μƒνƒœ μ˜μ‘΄λ©”μ„œλ“œκ°€ μžˆλŠ”κ²Œ μ‚¬μš©ν•˜κΈ° 훨씬 νŽΈν•˜λ‹€.

ν•˜μ§€λ§Œ μ™ΈλΆ€ 동기화 없이 μ—¬λŸ¬ μŠ€λ ˆλ“œκ°€ λ™μ‹œμ— μ ‘κ·Όν•  수 μžˆκ±°λ‚˜, μ‚¬μ΄λ“œ μ΄νŽ™νŠΈμ˜ 여지가 μžˆλ‹€λ©΄ μƒνƒœ μ˜μ‘΄λ©”μ„œλ“œ 없이 값을 λ°”λ‘œ μ „λ‹¬ν•˜κ±°λ‚˜ Optional등을 μ‚¬μš©ν•˜λŠ”κ²Œ μ’‹λ‹€. κ·Έ μž κΉλ™μ•ˆ 객체의 μƒνƒœκ°€ λ³€ν•  수 있기 λ•Œλ¬Έμ΄λ‹€.

 

 

# 볡ꡬ할 수 μžˆλ‹€λ©΄ check μ˜ˆμ™Έλ₯Ό, μ•„λ‹ˆλΌλ©΄ λŸ°νƒ€μž„ μ˜ˆμ™Έλ₯Ό λ°œμƒμ‚¬μš©ν•΄λΌ.

ν•΄λ‹Ή λ©”μ„œλ“œλ₯Ό μ‚¬μš©(호좜)ν•˜λŠ” μͺ½μ—μ„œ 볡ꡬλ₯Ό ν•΄μ•Όν•œλ‹€λ©΄ checked μ˜ˆμ™Έλ₯Ό μ‚¬μš©ν•˜λΌ.

그게 μ•„λ‹ˆλΌλ©΄ Runtime μ˜ˆμ™Έλ₯Ό λ°œμƒμ‹œμΌœ 였λ₯˜μž„을 빨리 μ•Œλ €λΌ. μ˜ˆμ™Έλ₯Ό 감싸 μ˜ˆμ™Έ λ‚΄μš©μ„ 계측에 맞게 μΆ”μƒν™”ν•˜λŠ” 것도 μ’‹λ‹€. DB μ—μ„œ SQL μ—λŸ¬κ°€ λ‚œκ±Έ, λΉ„μ¦ˆλ‹ˆμŠ€ μ„œλΉ„μŠ€μ—μ„œ κ·ΈλŒ€λ‘œ μ „λ‹¬ν•˜λŠ” κ±° 보닀 'μ„œλΉ„μŠ€ λ™μž‘ 였λ₯˜'둜 좔상화 μ‹œν‚€μž.

 

2021.07.07 - [🌱Backend/Java] - #7 Exception (μ˜ˆμ™Έμ²˜λ¦¬)

 

#7 Exception (μ˜ˆμ™Έμ²˜λ¦¬)

μžλ°”μ—μ„œμ˜ 였λ₯˜, μ˜ˆμ™Έ μžλ°”μ—μ„œ μ˜ˆμ™Έ, 였λ₯˜λŠ” java.lang.Throwable 클래슀λ₯Ό μƒμ†λ°›μœΌλ©° κ΅¬ν˜„ν•˜λ©° 3가지 μ’…λ₯˜λ‘œ λ‚˜λ‰œλ‹€. Error μžλ°” ν”„λ‘œκ·Έλž¨ λ°–μ—μ„œ λ°œμƒν•œ μ˜ˆμ™Έ. ν”„λ‘œμ„ΈμŠ€μ— 영ν–₯, 더 이상 μ‹€ν–‰ λΆˆκ°€

jiwondev.tistory.com

 

 

# ν•„μš”μ—†λŠ” 검사 μ˜ˆμ™ΈλŠ” μ‚¬μš©μ€ ν”Όν•˜λΌ.

κΌ­ μ˜ˆμ™Έλ₯Ό μ‚¬μš©ν•˜μ§€ μ•Šμ•„λ„ λ˜λŠ” λ©”μ„œλ“œκ°€ μžˆλ‹€. κ°€λŠ₯ν•˜λ©΄ Optionalμ΄λ‚˜ 검사 λ©”μ„œλ“œ(true, false)λ₯Ό λ°˜ν™˜ν•˜κ²Œ λ§Œλ“€μž.

try{ // κΈ°μ‘΄ μ½”λ“œ
    obj.action(args);

} catch(TheCheckedException e) {
    ...μ˜ˆμ™Έμƒν™©μ— λŒ€μ²˜ν•œλ‹€.
}
if(obj.actionPermitted(args)){ // μ˜ˆμ™Έλ₯Ό μ‚¬μš©ν•˜μ§€ μ•Šμ€ μ½”λ“œ
    obj.action(args);
}else{
    ...μ˜ˆμ™Έμƒν™©μ— λŒ€μ²˜ν•œλ‹€.
}

* 단 κ²€μ‚¬λ©”μ„œλ“œλŠ” λ©€ν‹°μŠ€λ ˆλ“œ ν™˜κ²½μ΄λ‚˜ side-effectκ°€ μžˆλ‹€λ©΄ μ‚¬μš©ν•˜μ§€λ§κ³ , λ°”λ‘œ κ°’μ΄λ‚˜ Optional을 λ°˜ν™˜ν•˜μž.

μ–΄λ–€ λ©”μ„œλ“œκ°€ check μ˜ˆμ™Έλ₯Ό λ˜μ§€λŠ” 것은, 이λ₯Ό ν˜ΈμΆœν•˜λŠ” μž…μž₯μ—μ„œ catch λΈ”λŸ­μ„ λ§Œλ“€μ–΄ μ˜ˆμ™Έλ₯Ό μ²˜λ¦¬ν•΄μ•Όν•˜λŠ” 뢀담이 생긴닀. λ˜ν•œ Java8 λΆ€ν„° μ‚¬μš©ν•˜λŠ” μŠ€νŠΈλ¦Όμ—μ„œλŠ” μ˜ˆμ™Έ λ˜μ§€λŠ” λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•˜κΈ° μ–΄λ ΅λ‹€.

 

 

# ν‘œμ€€ μ˜ˆμ™Έλ₯Ό μ‚¬μš©ν•˜λΌ.

μž¬μ‚¬μš©ν•˜κΈ° 쉽고, 가독성이 μ’‹μœΌλ©°, 클래슀λ₯Ό μ•ˆλ§Œλ“€μ–΄λ„ λ˜μ„œ μ„±λŠ₯도 쒋아진닀.

IllegalArgumentException ν—ˆμš©ν•˜μ§€ μ•ŠλŠ” 값이 인수둜 전달 됬을 λ•Œ
IllegalStateException 객체가 λ©”μ„œλ“œλ₯Ό μˆ˜ν–‰ν•˜κΈ° μ μ ˆν•˜μ§€ μ•Šμ„ λ•Œ (μΈμˆ˜κ°’μ΄ 무엇이든 μ–΄μ°¨ν”Ό μ‹€νŒ¨)
NullPointerException null을 ν—ˆμš©ν•˜μ§€ μ•ŠλŠ” λ©”μ„œλ“œμ— null을 건넸을 λ•Œ
IndexOutOfBoundsException μΈλ±μŠ€κ°€ λ²”μœ„λ₯Ό λ„˜μ—ˆμ„ λ•Œ
ConcurrentModificationException ν—ˆμš©ν•˜μ§€ μ•ŠλŠ” λ™μ‹œ μˆ˜μ •μ΄ λ°œκ²¬λ¬μ„ λ•Œ
UnsupportedOperationException ν˜ΈμΆœν•œ λ©”μ„œλ“œλ₯Ό μ§€μ›ν•˜μ§€ μ•Šμ„λ•Œ

λ‹€λ§Œ μƒμœ„ μ˜ˆμ™Έ(Exception, RuntimeException, Throwable, Error)λŠ” μž¬μ‚¬μš©ν•˜μ§€λ§μž. 이듀은 좔상 클래슀처럼 λ‹€λ₯Έ μ˜ˆμ™Έλ“€μ„ ν¬ν•¨ν•˜λŠ” μƒμœ„ ν΄λž˜μŠ€μ΄λ―€λ‘œ, μ•ˆμ •μ μœΌλ‘œ ν…ŒμŠ€νŠΈν•  수 μ—†λ‹€. λ§Œμ•½ μ μ ˆν•œ ν‘œμ€€ μ˜ˆμ™Έκ°€ μ—†λ‹€λ©΄ 상속받아 μƒˆλ‘­κ²Œ λ§Œλ“€μž.

 

참고둜 IllegalArgumentException κ³Ό IllegalStateException이 ν—·κ°ˆλ¦΄ 수 μžˆλŠ”λ°, 인수 값이 무엇이든 μ–΄μ°¨ν”Ό μ‹€νŒ¨ν–ˆμ„ 거라면 Stateλ₯Ό μ‚¬μš©ν•˜λ©΄ λœλ‹€. μ•„λ‹ˆλΌλ©΄ Argument.

 

 

# 좔상화 μˆ˜μ€€μ— λ§žλŠ” μ˜ˆμ™Έλ₯Ό 던져라.

λΉ„μ¦ˆλ‹ˆμŠ€ μ„œλΉ„μŠ€λ‹¨μ—μ„œ low-level의 λ©”λͺ¨λ¦¬ μ˜ˆμ™Έκ°€ λ‚˜μ˜¨λ‹€λ©΄ 맀우 λ‹Ήν™©μŠ€λŸ½κ³ , μ²˜λ¦¬ν•  방법도 μ—†λ‹€.

μ €μˆ˜μ€€μ˜ μ˜ˆμ™Έλ₯Ό μž‘μ•„μ„œ μƒˆλ‘œμš΄ μ˜ˆμ™Έλ‘œ 좔상화 μ‹œμΌœλΌ. 디버깅에 도움이 λœλ‹€λ©΄ μ˜ˆμ™Έ chain을 λ§Œλ“œλŠ”κ²Œ μ’‹λ‹€.

try{
    
} catch(LowerLevelException e){
    throw new HigherLevelException();
}
try{
    
} catch(LowerLevelException e){
    throw new HigherLevelException(e); // μ΄λ ‡κ²Œ κ°μ‹ΈλŠ” 것도 쒋은방법. μ˜ˆμ™Έμ˜ κ·Όλ³Έ 원인을 찾을 수 μžˆλ‹€.
}
class HigherLevelException extends Exception {
    HigherLevelException(Throwable throwable){
        super(throwable);
    }
}

 

 

# λ©”μ„œλ“œκ°€ λ˜μ§€λŠ” μ˜ˆμ™ΈλŠ” λͺ¨λ‘ λ¬Έμ„œν™”ν•΄λΌ.

λ©”μ„œλ“œκ°€ 던질 κ°€λŠ₯성이 μžˆλŠ” μ˜ˆμ™ΈλŠ” λͺ¨λ‘ λ¬Έμ„œν™”ν•˜λŠ”κ²Œ μ’‹λ‹€. 좔상 λ©”μ„œλ“œλ˜, λŸ°νƒ€μž„ λ©”μ„œλ“œλ˜ λ§ˆμ°¬κ°€μ§€μ΄λ‹€.

@throws λ˜λŠ” @Exception νƒœκ·Έλ₯Ό ν™œμš©ν•˜μž. 참고둜 이 λ‘κ°œμ˜ κΈ°λŠ₯은 κ°™λ‹€.

public class Sample {

    /**
     * 파일 μ“°κΈ°
     *
     * @throws FileNotFoundException μ§€μ •λœ νŒŒμΌμ„ 찾을 수 μ—†μŠ΅λ‹ˆλ‹€
     */
    public void writeToFile() throws FileNotFoundException {
    }

    /**
     * 파일 읽기
     *
     * @throws java.io.IOException         μž…μΆœλ ₯ κ΄€λ ¨
     * @throws java.lang.SecurityException λ³΄μ•ˆ κ΄€λ ¨
     */
    public void readFromFile() throws IOException, java.lang.SecurityException {
    }
}

μ‚¬μš©μž μž…μž₯μ—μ„œ μ΄λ ‡κ²Œ 확인할 수 μžˆλ‹€.

μ˜ˆμ™Έ μƒμ„Έλ©”μ‹œμ§€μ— μ‹€νŒ¨ κ΄€λ ¨ 정보λ₯Ό λ‹΄μœΌλ©΄ 더 μ’‹λ‹€.

// java9μ—μ„œλŠ” IndexOutOfBoundsλŠ” indexλ₯Ό 받도둝 μΆ”κ°€λ˜μ—ˆλ‹€. μ•„μ‰½κ²Œλ„ lower, upperλŠ” μ—†μŒ.
public IndexOutOfBoundsException(int lowerBound, int upperBound, int index) {
    super(String.format("μ΅œμ†Œκ°’: %d, μ΅œλŒ“κ°’: %d, 인덱슀: %d", lowerBound, upperBound, index))
    
    this.lowerBound = lowerBound;
    this.uppperBound = upperBound;
    this.index = index;
}

* 단 μ˜ˆμ™Έμ— λ³΄μ•ˆκ³Ό κ΄€λ ¨λœ 정보(계정등)λŠ” μ£Όμ˜ν•΄μ„œ λ‹€λ€„μ•Όν•œλ‹€. μŠ€νƒ 좔적 μ •λ³΄λŠ” λ§Žμ€ μ‚¬λžŒμ΄ λ³Ό 수 있기 λ•Œλ¬Έμ΄λ‹€.

 

 

# κ°€λŠ₯ν•˜λ©΄ μ‹€νŒ¨ μ›μžμ μœΌλ‘œ λ§Œλ“€μ–΄λΌ.

μ˜ˆμ™Έκ°€ λ°œμƒν–ˆλ‹€λ©΄, λ°œμƒν•˜κΈ° μ „ 상황을 μœ μ§€ν•˜λ„λ‘ λ§Œλ“€μ–΄λΌ.

일단 μˆ˜μ •ν•˜κ³  λ¬Έμ œκ°€ μžˆλ‹€λ©΄ μ˜ˆμ™Έλ₯Ό λ°œμƒμ‹œν‚€λŠ” 것 보닀 λ¨Όμ € 검사λ₯Ό ν•˜κ³  μˆ˜μ •ν•˜λŠ”κ²Œ μ’‹λ‹€.

public Object pop() {
    if(size == 0) //λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•˜κΈ°μ „ λ§€κ°œλ³€μˆ˜μ˜ μœ νš¨μ„±μ„ κ²€μ‚¬ν•˜μ—¬ μ‹€νŒ¨ν•  경우,μƒνƒœ 변경을 λ§‰λŠ”λ‹€!
        throw new EmptyStackException();
    
    Object result = elements[--size];
    elements[size] = null;
    return result;
}

이게 νž˜λ“€λ‹€λ©΄ 객체의 μž„μ‹œ λ³΅μ‚¬λ³Έμ—μ„œ μž‘μ—…μ„ μˆ˜ν–‰ν•œ λ‹€μŒ, μ„±κ³΅ν–ˆμ„ λ•Œ μ‹€μ œ 객체와 κ΅μ²΄ν•˜λŠ” 방법도 μžˆλ‹€.

λ¬Όλ‘  μ΄λ ‡κ²Œ ν•˜μ§€μ•Šκ³  λ”°λ‘œ 볡ꡬ μ½”λ“œλ₯Ό μž‘μ„±ν•˜μ—¬ μž‘μ—…ν•˜κΈ° μ „ μƒνƒœλ‘œ λŒλ¦¬λŠ” 방법도 μžˆμ§€λ§Œ, 쒋은 방법은 μ•„λ‹ˆλ‹€.

 

 

# μ˜ˆμ™Έλ₯Ό λ¬΄μ‹œν•˜μ§€ 마라.

λ»”ν•œ 이야기 κ°™μ§€λ§Œ, μ’…μ’… μΌμ–΄λ‚˜λŠ” 일이닀.

// catch 블둝을 λΉ„μ›Œλ‘λ©΄ μ˜ˆμ™Έκ°€ λ¬΄μ‹œλœλ‹€. μ•„μ£Ό μ˜μ‹¬μŠ€λŸ¬μš΄ μ½”λ“œλ‹€!
try {
    ...
} catch (SomeException e) {
    //아무 일도 ν•˜μ§€ μ•ŠμŒ.
}

이런 μ½”λ“œλŠ” μ‹€μ œ μ˜ˆμ™Έκ°€ λ°œμƒν–ˆμ„ λ•Œ, 이λ₯Ό μˆ¨κ²¨λ²„λ € 디버깅을 맀우 μ–΄λ ΅κ²Œ λ§Œλ“ λ‹€. μ‹¬ν•œ 경우 였λ₯˜λ₯Ό μ²˜λ¦¬ν•˜μ§€ μ•Šκ³  ν¬ν•¨ν•œ μ±„λ‘œ 앱이 λ™μž‘ν•˜λ‹€κ°€ 문제의 원인을 μ „ν˜€ λͺ¨λ₯΄λŠ” μƒνƒœλ‘œ κ°‘μžκΈ° 앱이 죽어버릴 수 μžˆλ‹€.

 

정말 νŠΉμˆ˜ν•œ κ²½μš°λΌμ„œ μ˜ˆμ™Έκ°€ ν•„μš”μ—†λ‹€λ©΄, κ·Έ 이유λ₯Ό μ£Όμ„μœΌλ‘œ 남기고 μ˜ˆμ™Έ λ³€μˆ˜μ˜ 이름을 ignored λ“±μœΌλ‘œ λͺ…μ‹œμ μœΌλ‘œ λ°›μ•„ λ‚˜νƒ€λ‚΄μž. λ˜ν•œ 둜그λ₯Ό ν™œμš©ν•΄ μ΅œμ†Œν•œμ˜ 디버깅 정보λ₯Ό λ‚¨κΈ°μž.

 

 

# κ³΅μœ μ€‘μΈ λ°μ΄ν„°λŠ” 동기화가 ν•„μˆ˜μ΄λ‹€. (λΆˆλ³€ 데이터인 κ²½μš°λŠ” ν•„μš”μ—†μŒ)

운영체제λ₯Ό λ°°μ› λ‹€λ©΄ μ•Œκ² μ§€λ§Œ, μ—¬λŸ¬ μŠ€λ ˆλ“œκ°€ λ™μ‹œμ— 값을 읽고 μˆ˜μ •ν•˜λŠ”κ±΄ 맀우 μœ„ν—˜ν•œ ν–‰μœ„μ΄λ‹€.

심지어 λ™κΈ°ν™”μ—μ„œ λ°œμƒν•œ λ²„κ·ΈλŠ” 디버깅 λ‚œμ΄λ„κ°€ λ†’μ€νŽΈμ΄λ‹€. κ°„ν—μ μœΌλ‘œ λ°œμƒν•˜κ±°λ‚˜, νŠΉμ • νƒ€μ΄λ°μ—λ§Œ λ°œμƒν•˜κ³  VM의 μ΅œμ ν™”μ— 따라 λ‹€λ₯΄κ²Œ λ™μž‘ν•˜κΈ°λ„ ν•˜κΈ° λ•Œλ¬Έμ΄λ‹€.

2021.07.28 - [πŸ‘€κΈ°λ³Έ 지식/CS 기본지식] - Javaμ—μ„œμ˜ μž„κ³„μ˜μ—­κ³Ό λ°λ“œλ½ ν•΄κ²° (sync, λͺ¨λ‹ˆν„°)

2021.07.27 - [πŸ‘€κΈ°λ³Έ 지식/Java 기본지식] - μ“°λ ˆλ“œ 동기화 (sync, volatile, AtomicClass)

 

μ“°λ ˆλ“œ 동기화 (sync, volatile, AtomicClass)

μ“°λ ˆλ“œμ˜ 동기화 : ν•œ μ“°λ ˆλ“œκ°€ 진행쀑인 μž‘μ—…μ„ λ‹€λ₯Έ μ“°λ ˆλ“œκ°€ κ°„μ„œν•˜μ§€ λͺ»ν•˜κ²Œ ν•˜λŠ” 것. μ“°λ ˆλ“œλŠ” class의 멀버 λ³€μˆ˜(μžμ›)을 μ‚¬μš©ν•œλ‹€. λ©€ν‹°μ“°λ ˆλ“œ ν™˜κ²½μ—μ„œλŠ” μ“°λ ˆλ“œ 동기화λ₯Ό ν•˜μ§€μ•ŠμœΌλ©΄, 심

jiwondev.tistory.com

 

 

# κ³Όλ„ν•œ λ™κΈ°ν™”λŠ” ν”Όν•˜λΌ

λ™κΈ°ν™”λŠ” μŠ€λ ˆλ“œλ₯Ό λΈ”λŸ¬ν‚Ήν•˜μ—¬ μ„±λŠ₯을 μ‹¬ν•˜κ²Œ λ–¨μ–΄λœ¨λ¦¬κ³ , κ΅μ°©μƒνƒœμ— λΉ νŠΈλ¦°λ‹€.

κΌ­ ν•„μš”ν•œ κ³³μ—λ§Œ μ‚¬μš©ν•˜μž. synchronziedλŠ” λ©”μ„œλ“œλ‹¨μœ„κ°€ μ•„λ‹Œ, { ~ } λΈ”λŸ­ λ‹¨μœ„λ‘œλ„ μ‚¬μš©ν•  수 μžˆλ‹€.

 

κ³Όλ„ν•œ λ™κΈ°ν™”λŠ” λ³‘λ ¬λ‘œ μ‹€ν–‰ν•  기회λ₯Ό μžƒκ³ , λͺ¨λ“  μ½”μ–΄κ°€ λ©”λͺ¨λ¦¬λ₯Ό μΌκ΄€λ˜κ²Œ 보기 μœ„ν•œ μ§€μ—°μ‹œκ°„(λΈ”λ‘œν‚Ή)이 μ§„μ§œ λΉ„μš©μ΄λ‹€. λ˜ν•œ JVM의 μ½”λ“œ μ΅œμ ν™”λ₯Ό μ œν•œν•˜λŠ” 것도 κ³ λ €ν•΄μ•Ό ν•œλ‹€.

 

λ”°λΌμ„œ κ°€λ³€ 클래슀λ₯Ό μž‘μ„±ν•  λ•ŒλŠ” 두 가지 선택을 ν•  수 μžˆλ‹€.

첫 λ²ˆμ§ΈλŠ” 동기화λ₯Ό ν•˜μ§€ μ•Šκ³ , κ·Έ 클래슀λ₯Ό μ‚¬μš©ν•΄μ•Ό ν•˜λŠ” ν΄λž˜μŠ€κ°€ μ™ΈλΆ€μ—μ„œ λ™κΈ°ν™”ν•˜λŠ” 것이닀.

두 λ²ˆμ§ΈλŠ” 동기화λ₯Ό λ‚΄λΆ€μ—μ„œ μˆ˜ν–‰ν•΄ thread-safe ν•œ 클래슀둜 λ§Œλ“œλŠ” 것이닀. λ‹€λ§Œ ν΄λΌμ΄μ–ΈνŠΈκ°€ μ™ΈλΆ€μ—μ„œ 객체 전체에 락을 κ±°λŠ” 것보닀 λ™μ‹œμ„±μ„ κ°œμ„ ν•  수 μžˆμ„ λ•Œ 선택해야 ν•œλ‹€. μ˜ˆλ‘œλŠ” java.util.concurrent νŒ¨ν‚€μ§€κ°€ μžˆλ‹€.

 

java.util.Random도 λ§ˆμ°¬κ°€μ§€λ‹€. λ™κΈ°ν™”ν•˜μ§€ μ•ŠλŠ” 버전인 ThreadLocalRandom으둜 λŒ€μ²΄λ˜μ—ˆλ‹€.

μ„ νƒν•˜κΈ° μ–΄λ ΅λ‹€λ©΄ λ™κΈ°ν™”ν•˜μ§€ 말고, λŒ€μ‹  λ¬Έμ„œμ— “μŠ€λ ˆλ“œ μ•ˆμ „ν•˜μ§€ μ•Šλ‹€” κ³  λͺ…μ‹œν•˜μž.

 

 

# μŠ€λ ˆλ“œλ₯Ό 직접 λ§Œλ“€μ§€λ§κ³  μ‹€ν–‰μž, ν…ŒμŠ€ν¬, μŠ€νŠΈλ¦Όμ„ μ΄μš©ν•˜λΌ.

μŠ€λ ˆλ“œλ₯Ό 직접 λ‹€λ£° 수 μžˆμ§€λ§Œ concurrent νŒ¨ν‚€μ§€λ₯Ό μ΄μš©ν•˜λ©΄ κ°„λ‹¨ν•˜κ²Œ μ½”λ“œλ₯Ό μž‘μ„±ν•  수 μžˆλ‹€.

μ΄λŠ” μ‚¬μš©μ„± λΏλ§Œμ•„λ‹ˆλΌ μž‘μ—… λ‹¨μœ„μ™€ μ‹€ν–‰ λ§€μ»€λ‹ˆμ¦˜μ„ λΆ„λ¦¬ν•˜λŠ” νš¨κ³Όλ„ μžˆλ‹€. 

java.util.concurrent νŒ¨ν‚€μ§€ μ•ˆμ—λŠ” μ‹€ν–‰μž ν”„λ ˆμž„μ›Œν¬(Executor Framework)κ°€ μžˆλ‹€.

// 큐λ₯Ό μƒμ„±ν•œλ‹€.
ExecutorService exec = Executors.newSingleThreadExecutor();

// νƒœμŠ€ν¬ μ‹€ν–‰
exec.execute(runnable);

// μ‹€ν–‰μž μ’…λ£Œ
exec.shutdown();

https://madplay.github.io/post/prefer-executors-tasks-and-streams-to-threads

 

 

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

λ°˜ν™˜ νƒ€μž…λ§ŒμœΌλ‘œ μ •ν™•νžˆ μ•Œ μˆ˜μ—†λ‹€λ©΄, (νŒ©ν† λ¦¬ λ©”μ„œλ“œ) μ•„λž˜ 처럼 μžμ‹ μ΄ λ°˜ν™˜ν•˜λŠ” 객체에 λŒ€ν•œ μŠ€λ ˆλ“œ μ•ˆμ •μ„±μ„ λ¬Έμ„œν™”ν•˜λŠ” 것이 μ’‹λ‹€. λ‹¨μˆœνžˆ synchronized ν‚€μ›Œλ“œκ°€ λΆ™μ—ˆλ‹€κ³ ν•΄μ„œ, λͺ¨λ“  μƒν™©μ—μ„œ μ•ˆμ „ν•˜λ‹€λŠ” 보μž₯은 ν•  수 μ—†κΈ° λ•Œλ¬Έμ΄λ‹€.

 

Collections.synchronziedMap 의 API λ¬Έμ„œμ—λŠ” μ•„λž˜μ™€ 같이 λͺ…μ‹œλ˜μ–΄ μžˆλ‹€.

/**
 * It is imperative that the user manually synchronize on the returned
 * map when iterating over any of its collection views
 * λ°˜ν™˜λœ 맡의 μ½œλ ‰μ…˜ λ·°λ₯Ό μˆœνšŒν•  λ•Œ λ°˜λ“œμ‹œ κ·Έ 맡으둜 μˆ˜λ™ λ™κΈ°ν™”ν•˜λΌ
 * 
 *  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());
 *  }
 */
  • λΆˆλ³€(immutable)
    • ➑ ν•΄λ‹Ή 클래슀의 μΈμŠ€ν„΄μŠ€λŠ” 마치 μƒμˆ˜μ™€λ„ κ°™μ•„μ„œ μ™ΈλΆ€ 동기화도 ν•„μš” μ—†μŠ΅λ‹ˆλ‹€.
    • ➑ 예λ₯Ό λ“€λ©΄ String, Long, BigInteger
  • 무쑰건적인 μŠ€λ ˆλ“œ μ•ˆμ „(unconditionally thread-safe)
    • ➑ ν•΄λ‹Ή 클래슀의 μΈμŠ€ν„΄μŠ€λŠ” μˆ˜μ •λ  수 μžˆμ§€λ§Œ λ‚΄λΆ€μ—μ„œλ„ μΆ©μ‹€νžˆ λ™κΈ°ν™”ν•˜μ—¬ λ³„λ„μ˜ μ™ΈλΆ€ 동기화없이 λ™μ‹œμ— μ‚¬μš©ν•΄λ„ μ•ˆμ „ν•©λ‹ˆλ‹€.
    • ➑ 예λ₯Ό λ“€λ©΄ AtomicLong, ConcurrentHashMap
  • 쑰건뢀 μŠ€λ ˆλ“œ μ•ˆμ „(conditionally thread-safe)
    • ➑ 무쑰건적인 μŠ€λ ˆλ“œ μ•ˆμ „μ„±κ³Ό κ°™μ§€λ§Œ 일뢀 λ©”μ„œλ“œλŠ” λ™μ‹œμ— μ‚¬μš©ν•˜λ €λ©΄ μ™ΈλΆ€ 동기화가 ν•„μš”ν•©λ‹ˆλ‹€.
    • ➑ Collections.synchronized λž˜νΌ λ©”μ„œλ“œκ°€ λ°˜ν™˜ν•œ μ»¬λ ‰μ…˜
  • μŠ€λ ˆλ“œ μ•ˆμ „ν•˜μ§€ μ•ŠμŒ(not thread-safe)
    • ➑ ν•΄λ‹Ή 클래슀의 μΈμŠ€ν„΄μŠ€λŠ” μˆ˜μ •λ  수 있으며 λ™μ‹œμ— μ‚¬μš©ν•˜λ €λ©΄ 각각의 λ©”μ„œλ“œ ν˜ΈμΆœμ„ ν΄λΌμ΄μ–ΈνŠΈκ°€ μ„ νƒν•œ μ™ΈλΆ€ 동기화 둜직으둜 감싸야 ν•œλ‹€.
    • ➑ 예λ₯Ό λ“€λ©΄ ArrayList, HashMap
  • μŠ€λ ˆλ“œ μ λŒ€μ (thread-hostile)
    • ➑ μ™ΈλΆ€ λ™κΈ°ν™”λ‘œ 감싸더라도 λ©€ν‹°μŠ€λ ˆλ“œ ν™˜κ²½μ—μ„œ μ•ˆμ „ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.
    • ➑ μ΄λŸ¬ν•œ ν΄λž˜μŠ€λŠ” λ™μ‹œμ„±μ„ κ³ λ €ν•˜μ§€ μ•Šκ³  λ§Œλ“€λ‹€λ³΄λ©΄ μš°μ—°νžˆ λ§Œλ“€μ–΄μ§‘λ‹ˆλ‹€.

 

 

# 지연 μ΄ˆκΈ°ν™”λŠ” μ‹ μ€‘νžˆ μ‚¬μš©ν•˜λΌ.

λ°˜λŒ€λ‘œ λ§ν•˜λ©΄ κΌ­ ν•„μš”ν•˜κΈ° μ „κΉŒμ§€λŠ” μ‚¬μš©ν•˜μ§€ λ§λΌλŠ” κ±°λ‹€. 지연 μ΄ˆκΈ°ν™”λŠ” μ–‘λ‚ μ˜ 검이닀.

μ΄ˆκΈ°ν™”μ— λ“œλŠ” λΉ„μš©, μ΄ˆκΈ°ν™”λœ ν•„λ“œμ˜ 호좜 λΉˆλ„μˆ˜μ— 따라 지연 μ΄ˆκΈ°ν™”κ°€ 였히렀 μ„±λŠ₯을 μ•…ν™”μ‹œν‚¬ 수 μžˆλ‹€. 크게 ν•„μš”ν•œ 상황이 μ•„λ‹ˆλΌλ©΄, 일반적인 μ΄ˆκΈ°ν™”κ°€ 훨씬 λ‚«λ‹€.

// 일반적인 μΈμŠ€ν„΄μŠ€ ν•„λ“œ μ΄ˆκΈ°ν™” 방법
private final FieldType field = computeFieldValue();

 

 

μ„±λŠ₯ λ•Œλ¬Έμ— 정적 ν•„λ“œλ₯Ό μ΄ˆκΈ°ν™”ν•΄μ•Όν•œλ‹€λ©΄, λ‚΄λΆ€ Holder ν΄λž˜μŠ€λ‚˜ Double-check κ΄€μš©κ΅¬λ₯Ό μ‚¬μš©ν•˜μž.

 

2-3 Singleton (싱글톀 νŒ¨ν„΄)

# 싱글톀 νŒ¨ν„΄ ν”„λ‘œμ„ΈμŠ€μ— 객체의 μΈμŠ€ν„΄μŠ€κ°€ 단 ν•˜λ‚˜λ§Œ μ‘΄μž¬ν•˜λ„λ‘ λ§Œλ“œλŠ” νŒ¨ν„΄. β­• Static λ©”λͺ¨λ¦¬ μ˜μ—­μ— 객체λ₯Ό ν•˜λ‚˜λ§Œ 생성, μž¬μ‚¬μš©ν•¨μœΌλ‘œμ„œ λ©”λͺ¨λ¦¬ λ‚­λΉ„λ₯Ό λ°©μ§€ν•œλ‹€. β­• 객체λ₯Ό μƒˆλ‘œ μƒμ„±ν•˜

jiwondev.tistory.com

public MyClass{

    private static FieldType getField() { 
        return FieldHolder.field;
    }

    private static class FieldHolder {
        static final FieldType field = computeFieldValue();
    }
}

 

 

# ν”„λ‘œκ·Έλž¨μ˜ λ™μž‘μ„ μŠ€λ ˆλ“œ μŠ€μΌ€μ₯΄λŸ¬μ— κΈ°λŒ€μ§€ 말라.

μŠ€μΌ€μ₯΄λŸ¬ 정책은 μš΄μ˜μ²΄μ œλ§ˆλ‹€ λ‹€λ₯Ό 수 μžˆλ‹€. Thread.yield(λ‹€λ₯Έ μŠ€λ ˆλ“œμ—κ²Œ 싀행을 μ–‘λ³΄ν•˜λŠ” λ©”μ„œλ“œ) 처럼 μŠ€λ ˆλ“œ μš°μ„ μˆœμœ„λ₯Ό λ°”κΎΈλŠ”κ±΄ μŠ€μΌ€μ₯΄λŸ¬μ— μ œκ³΅ν•˜λŠ” '힌트' 일 뿐이지, λ°˜λ“œμ‹œ κ·Έλ ‡κ²Œ λ™μž‘ν• κ±°λΌλŠ” 보μž₯은 μ—†λ‹€. μžλ°”μ˜ μŠ€λ ˆλ“œ μš°μ„ μˆœμœ„λŠ” 운영체제 이식성이 λ‚˜μœνŽΈμ΄λ‹€.

 

λ˜ν•œ μŠ€λ ˆλ“œλŠ” μ ˆλŒ€λ‘œ busy-waiting ν•œ μƒνƒœκ°€ 되면 μ•ˆλœλ‹€. ν•΄λ‹Ή μŠ€λ ˆλ“œ λ•Œλ¬Έμ— μ•± μ „μ²΄μ—μ„œ λ‹€λ₯Έ μœ μš©ν•œ μž‘μ—…μ΄ 싀행될 κΈ°νšŒκ°€ λ°•νƒˆλ¨μ„ μΈμ§€ν•˜κ³  있자.

// Busy-waiting μ½”λ“œ. μ§€κΈˆ μž‘λ™ν•΄λ„ λ˜λŠ”μ§€ μ—¬λΆ€λ₯Ό κ³„μ†ν•΄μ„œ μŠ€λ ˆλ“œκ°€ 슀슀둜 검사함.
public class SlowCountDownLatch {
    private int count;

    public SlowCountDownLatch(int count) {
        if (count < 0)
            throw new IllegalArgumentException(count + " < 0");
        this.count = count;
    }

    public void await() {
        while (true) { // μ§„μ§œ μ΄λ ‡κ²Œ 할건가?
            synchronized(this) {
                if (count == 0)
                    return;
            }
        }
    }

    public synchronized void countDown() {
        if (count != 0)
            count--;
    }
}

 

λΈ”λ‘œκ·Έμ˜ 정보

JiwonDev

JiwonDev

ν™œλ™ν•˜κΈ°