본문 바로가기
프로그래밍 놀이터/디자인 패턴, 리펙토링

[Effective Java] 공유하는 가변 데이터에 접근 시 동기화하자.

by 돼지왕 왕돼지 2017. 3. 7.
반응형

 [Effective Java] 공유하는 가변 데이터에 접근 시 동기화하자.


++, atomic long, atomiclong, Double, Effective JAVA, effectively immutable, Final, hoisting, liveness failure, long, safe publication, safety failure, static, synchronized, thread.stop, vm, volatile, [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 변경자를 사용하는 것도 좋다.
그러나 올바르게 사용하려면 잘 알아야 한다.





반응형

댓글