[Effective Java] 공유하는 가변 데이터에 접근 시 동기화하자. |
-
동기화를 하지 않으면, 하나의 스레드에서 변경한 내용을 다른 스레드에서 못 볼 수 있다.
동기화는 불안정 상태의 객체를 스레드가 볼 수 없도록 하는 것은 물론, 동기화된 메소드나 블록에 진입하는 각 스레드가 앞에서의 모든 변경(같은 락으로 보호되었던)이 반영된 결과를 볼 수 있게 해준다.
-
자바 언어 명세에서는 long 이나 double 타입이 아닌 변수의 값을 읽거나 쓸 때는 원자성을 보장한다.
long 이나 double 타입이 아닌 변수의 값을 읽으면 어떤 스레드가 그 변수에 저장했던 값을 반환 받을 수 있다는 것.
실제로 동기화하지 않고 여러 스레드가 그 변수를 동시에 수정해도 그때그때 수정된 값이 반영된다.
스레드가 필드 값을 읽을 때 아무 값이나 받는 것은 아니라는 것을 보장한다.
그러나 한 스레드가 변경한 값을 다른 스레드가 꼭 볼 수 있다는 것은 보장하지 않는다.
동기화는 상호 배타는 물론, 스레드 간의 신뢰성 있는 변수 값 전달에도 필요하다.
-
cf) Thread.stop 을 사용하지 말자. ( 스레드 안전하지 않다. )
-
호이스팅(hoisting) 이라는 최적화가 있는데,
synchronized 가 제대로 안 되어 있으면 이상한 코드로 변환시켜 활동 실패(liveness failure) 를 만들기도 한다.
이런 문제를 해결하는 한 가지 방법은 synchronized 로 제대로 동기화하는 것이다.
-
동기화 시킬 때 값을 변경하는 메소드만 동기화하면 안 된다!!!
읽는 오퍼레이션과 변경하는 오퍼레이션 모두 동기화되어야만 동기화 효과가 생긴다!!
-
volatile 은 상호 배타를 수행하지는 않지만, 그 필드의 값을 읽는 스레드에서는 가장 최근에 변경된 값을 볼 수 있다.
-
volatile 의 문제점은 증감 연산자(++) 가 원자적이 아니라는 것이다.
안전 실패 ( safety failure )를 발생시킨다.
-
synchronized 나 volatile 을 사용하는 것도 한 방법이지만, AtomicLong 과 같은 동기화된 data 를 사용할 수도 있다.
-
동기화 이슈를 피하는 가장 좋은 방법은
가변 데이터를 공유하지 않는 것이다.
불변 데이터를 공유하거나 아예 공유하지 말자.
가변 데이터의 사용은 단일 스레드로 제한해야 좋다.
-
객체 참조를 공유하는 행위만 동기화하면서, 한 스레드가 짧은시간 동안만 데이터 객체를 변경한 후 그 객체 참조를 다른 스레드와 공유하는 것은 수용할만 하다.
그러면 그 객체가 다시 변경되지 않는 한, 다른 스레드들이 더 이상의 동기화를 하지 않고 그 객체를 읽을 수 있다.
그런 객체를 효율적인 가변 객체(effectively immutable)이라고 한다. ( 공유되기 전에 모든 변경이 일어나도록 한 것 )
-
효율적인 가변 객체(effectively immutable) 참조를 하나의 스레드에서 다른 스레드로 전달하는 것을 안전 출판(safe publication)이라고 한다.
안전 출판 방법은, 객체 참조를 클래스 초기화 시 static 필드에 저장할 수 있고, 락을 사용해서 접근하는 volatile 필드나 final 필드 및 일반 필드에 저장할 수 있다.
또는 동시성이 지원되는 컬렉션에 저장하는 방법도 있다.
Summary
여러 스레드가 가변 데이터를 공유할 때, 그 데이터를 읽거나 쓰는 각 스레드에서는 반드시 동기화를 해야 한다.
동기화를 하지 않으면 한 스레드가 변경한 것을 다른 스레드가 볼 수 있다는 보장이 없다.
공유되는 가변 데이터를 동기화하는데 실패하면, 활동 실패(liveness failure)나 안전 실패(safety failure)가 생길 수 있다.
이런 실패들은 결함을 찾기 가장 어려운 것들이며, 간헐적으로 발생할 수 있고, 어떤 VM 을 사용하느냐에 따라 동작이 많이 달라질 수 있다.
만일 스레드 간의 확실한 변수 값 전달만이 필요하고 상호 배타 처리를 할 필요가 없다면, volatile 변경자를 사용하는 것도 좋다.
그러나 올바르게 사용하려면 잘 알아야 한다.
'프로그래밍 놀이터 > 디자인 패턴, 리펙토링' 카테고리의 다른 글
[Effective Java] 스레드 그룹보다는 실행자와 작업을 사용하자. (0) | 2017.03.10 |
---|---|
[Effective Java] 지나친 동기화는 피하자 (0) | 2017.03.09 |
[Effective Java] 예외를 묵살하지 말자. (0) | 2017.03.06 |
[Effective Java] 실패 원자성을 갖도록 노력하자 (0) | 2017.03.03 |
[Effective Java] 실패 상황 정보를 상세 메세지에 포함하자. (0) | 2017.03.02 |
댓글