NMP-Study / EffectiveJava2018

Effective Java Study
9 stars 0 forks source link

아이템 78. 공유 중인 가변 데이터는 동기화해 사용하라 #78

Closed madplay closed 5 years ago

fairsun commented 5 years ago

synchronized 키워드

원자적(atomic)

메모리 모델

잘못된 코드 - 이 프로그램은 얼마나 오래 실행될까?

public class StopThread { private static boolean stopRequested;

public static void main(String[] args) throws InterruptedException {
    Thread backgroundThread = new Thread(() -> {
       int i = 0;
       while(!stopRequested) {
           System.out.println("StopThread.main: " + i);
            i++;
       }
    });

    backgroundThread.start();

    TimeUnit.SECONDS.sleep(1);

    stopRequested = true;
}

}


- 동기화하지 않으면 메인 스레드가 수정한 값을 백그라운드 스레드가 언제쯤에나 보게 될지 보증할 수 없다.
- 동기화가 빠지면 가상머신이 다음과 같은 최적화를 수행할 수도 있는 것이다.
~~~java
           if(!stopRequested) {
               while (true) {
                   i++;
               }
           }

적절히 동기화해 스레드가 정상 종료한다.

public class StopThread { private static boolean stopRequested;

private static synchronized void requestStop() {
    stopRequested = true;
}

private static synchronized boolean stopRequested() {
    return stopRequested;
}

public static void main(String[] args) throws InterruptedException {
    Thread backgroundThread = new Thread(() -> {
       int i = 0;
        while(!stopRequested()) {
           i++;
       }
    });

    backgroundThread.start();

    TimeUnit.SECONDS.sleep(1);

    requestStop();
}

}


### volatile 필드를 사용해 스레드가 정상 종료한다.
- 배타적 수행과는 상관없지만 항상 가장 최근에 기록된 값을 읽게 됨을 보장한다.
- 속도가 더 빠른 대안
~~~java
import java.util.concurrent.TimeUnit;

public class StopThread {
    private static volatile boolean stopRequested;

    public static void main(String[] args) throws InterruptedException {
        Thread backgroundThread = new Thread(() -> {
           int i = 0;
            while(stopRequested) {
               i++;
           }
        });

        backgroundThread.start();

        TimeUnit.SECONDS.sleep(1);

        stopRequested = true;
    }
}

public static int generateSerialNumber() { return nextSerialNumber++;; }


#### java.util.concurrent.atomic을 이용한 lock-free 동기화(#59)
- AtomicLong :  thread safe 
- 통신 + 배타적 실행
- 성능도 우수
~~~~java
private static final AtomicLong nextSerialNumber = new AtomicLong();

public static long generateSerialNumber() {
    return nextSerialNumber.getAndIncrement();
}

결론