78. 변경 가능한 공유 데이터는 동기화하라
synchronized
synchronized 키워드는 해당 메서드나 블록을 한번에 한 스레드씩 수행하도록 보장한다.
2가지 주요 기능
배타적 수행
한 쓰레드가 사용시 다른 쓰레드가 들어오지 못하게 막는다.
스레드 간 안정적인 통신
일관성이 깨진 상태를 read 하는 것을 막아준다.
문제가 있는 코드
아래 코드는 1초후에 종료가 될까?
// 예상과 다르게 작동하는 코드
public class StopThread {
private static 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;
}
}이유
메인스레드가 수정한 값을 백그라운드 스레드가 언제쯤에나 보게될지 보증 불가 하다.
그리고 JVM은 다음과 같이 최적화를 수행하기도 한다.
개선된 코드
쓰기와 읽기 메소드 모두 synchronized 필요
더 나은 대안
위 반복문에서 지속적인 동기화를 함으로, 느릴 수 있다.
volatile 를 사용하자.
항상 최근에 기록된 값을 읽도록 보장
volatile 사용시 주의사항
아래 코드 예제는 유일한 일련번호를 리턴해준다.
generateSerialNumber를 한 스레드가 접근했을때, nextSerialNumber을 읽고/쓰는 작업을 수행한다.
그런데, 중간에 다른 스레드가 비집고 와서 실행된다면?
같은 값을 받게 된다.
발생 이유
volatile 은 동기화의 2가지 기능 중 통신 쪽만 지원하기에 베타적 수행은 해결해주지 않는다.
해결법
메소드에 synchronized 추가하고 변수에 volatile 은 제거
추가적으로, int 대신 long 사용이 더 견고함
아이템59 조언에 따라 - atomic
java.util.concurrent.atomic 패키지를 사용해보자.
락없이도 안전
조언
mutable 데이터는 공유하지 않는 것이 좋다. 즉, 가변 데이터는 단일 스레드에서만 쓰자.
Last updated