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

[Effective Java] 지나친 동기화는 피하자

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

 [Effective Java] 지나친 동기화는 피하자


alien method, arralist, concurrent collection, copyonwritearraylist, CPU, cpu 시간, dead lock, Effective JAVA, list, reentrant, static 필드, vm 능력 제한, [Effective Java] 지나친 동기화는 피하자, 가변 클래스, 객체 전체에 락, 결정 사항, 과도한 동기화, 교착 상태, 내부 배열, 내부적, 단일 스레드, 데이터 손상, 동기화, 동기화된 영역, 동기화의 실제 비용, 동시성 지원 컬렉션, 등기화, 락, 락 획득 소요시간, 멀티 코어, 멀티 코어 cpu, 메모리, 메소드, 명확하게, 문서화, 변이, 병렬처리 기회 상실, 복사본, 블록, 비용, 빈번, 성능, 순환, 숨은 비용, 스레드 안전한 가변 클래스, 쓰기 처리, 안전 실패, 예외, 오버라이딩, 외계인 메소드, 외계인 메소드 호출, 외부적으로, 외부적으로 동기화, 외부적인 동기화 수행, 요금, 요소 변경, 이질적인 녀석, 일치된 뷰, 자바 락, 자체적, 자체적 동기화, 재진입성, 적절한 곳, 제공하는 메소드, 제어, 제어권, 제한, 지나친 동기화, 지연, 코드 실행 최적화, 클라이언트, 클래스, 함수 객체, 호출, 활동 실패


-
지나친 동기화는 성능을 저하시키고 교착상태(dead lock)을 유발시키며, 심지어 예기치 않은 행동을 초래할 수 있다.


-
동기화된 메소드나 블록 안에서 절대로 클라이언트에게 제어권을 넘기면 안 된다.
즉, 동기화된 영역 내부에서는 오버라이딩된 메소드를 호출하지 않아야 하며, 함수 객체의 형태로 클라이언트가 제공하는 메소드도 호출하지 말아야 한다.
동기화된 영역을 갖는 클래스의 관점에서 그런 메소드들은 매우 이질적인 녀석들이다.
그 메소드가 무슨 일을 하는지 알지 못하며, 이질적인 일을 하는 것을 제어하지도 못한다.
외계인 메소드가 하는 일에 따라 다르겠지만, 동기화된 영역에서 그 메소드를 호출하면 예외나 dead lock 또는 데이터 손상까지 초래할 수 있다.


-
자바의 락은 재진입성(reentrant)을 가지므로, 다른 언어에 비해 dead lock 에 빠질 확률은 낮다.
그러나 결과는 더 큰 재앙으로 나타날 수도 있다.
활동실패를 안전실패로 바꾸는 결과를 초래하기 때문이다.


-
동기화된 블록 밖으로 외계인 메소드 호출을 옮기는 좋은 방법이 있다.
동시성 지원 컬렉션(concurrent collection) 인 CopyOnWriteArrayList 클래스를 사용하면 된다.
CopyOnWriteArrayList 는 ArrayList 의 변이로, 근간이 되는 내부 배열 전체에 대해 새로운 복사본을 만들어 모든 쓰기 처리를 수행한다.
대부분의 사용에서 CopyOnWriteArrayList 의 성능은 형편없지만, List 의 요소 변경은 거의 없고 전체 요소 순환 처리는 빈번한 경우에 사용하면 딱 맞다.


-
일반적으로 동기화된 블록 내부에서는 가능한 적은 양의 일을 처리해야 한다.
락을 획득하고, 공유 데이터를 조사하고, 필요 시 그 데이터를 변환하고 락을 해지하자.


-
자바 동기화 비용은 급격하게 하락하고 있는 반면,
지나친 동기화를 하지 않는 것은 더욱 중요하게 되었다.
멀티 코어 CPU가 보편화되면서, 락을 획득하는데 소요되는 CPU 시간은 지나친 동기화에 들어가는 실제 비용이 아닌 시대가 되었다.
병렬처리의 기회 상실, 모든 코어가 메모리의 일치된 뷰를 가져야 하는 요구에 따른 지연 등이 지나친 동기화의 실제 비용이다.
지나친 동기화의 또 다른 숨은 비용은 과도한 동기화 코드로 인해 코드 실행을 최적화하는 VM 의 능력을 제한할 수 있다는 것.


-
동시적인 사용이 필요하고, 내부적(자체적)으로 동기화를 함으로써 외부적으로(클라이언트에 의해) 객체 전체에 락을 사용하는 것보다 훨씬 더 높은 동시성을 성취하려면, 스레드에 안전한 가변 클래스를 만들어야 한다.
그렇지 않으면, 내부적으로 동기화하지 말고 클라이언트가 적절한 곳에서 외부적으로 동기화하게 하자.


-
만일 메소드에서 static 필드를 변경한다면 그 필드에 대한 접근을 반드시 동기화해야 한다.
일반적으로 그 메소드가 단일 스레드에서만 사용되더라도 마찬가지다.
그런 메소드에 대해 클라이언트가 외부적인 동기화를 수행하는 것은 불가능하다.



Summary


동기화된 영역 안에서 외계인 메소드를 절대 호출하지 말자.
dead lock 이나 데이터 손상을 가져오기 쉽다.
동기화된 영역 안에서 해야 할 일의 양을 제한해야 한다.
가변 클래스를 설계할 때는 자체적으로 동기화해야 하는지 잘 생각하자.
멀티 코어 CPU 가 보편화된 요즘은 동기화를 지나치게 하지 않는 것이 더 중요하다.
타당한 이유가 있을 때에 한해서 우리 클래스를 내부적으로 동기화하자.
그리고 결정 사항을 명확하게 문서화하자.





반응형

댓글