개발하는 두더지

[Effective Java 규칙 67] 과도한 동기화는 피하라 본문

Java,Android

[Effective Java 규칙 67] 과도한 동기화는 피하라

덜지 2018. 11. 13. 14:03

[Effective Java 규칙67] 과도한 동기화는 피하라 

Effective Java 2/E 책과 구글링을 통해 내용을 정리하고 개인적인 견해가 포함된 글입니다.


동기화를 너무 과도하게 적용하면 성능저하, 교착상태 문제가 생길 수 있다. 


예를들어 옵저버 패턴에서 0~100인 값을 발행하고 전파한다고 생각해보자.

동기화 블록안의 순환문에서 등록된 옵저버에 순차적으로 값을 등록하고 전파한다. 특정 인덱스에서 전파를 받는 콜백 인터페이스 함수에서 만약 옵저버를 삭제한다면 어떻게 될까? 


리스트 순회가 이루어지는 상황에서 리스트에서 원소를 삭제하려하기 때문에 ConcurrentModificationException이 발생한다.


executor service를 이용하여 다른 스레드에서 구독해제를 시도한다면 어떻게 될까?

예외가 발생하지 않는 반면에 교착상태가 발생한다. 다른 스레드에서 옵저버 삭제를 호출하고 옵저버에 락을 걸려고하는데 주 스레드가 이미 락을 걸고 있어서 락을 걸 수 가없다. 


이런 오류들을 해결하는 방법은 옵저버 리스트의 복사본을 만들어서 락 없이도 안전하게 리스트를 순회할수 있도록 바꾸는 것이다. 그러면 더이상 예외나 교착상태가 일어나지 않는다.


ArrayList의 경우 스레드에 안전하게 설계되어있지 않아서 스레드 처리가 필요한 List의 경우 Vector를 사용하거나 ArrayList에 synchronized 키워드를 사용해서 해결했다. 하지만 릴리즈 1.5부터 자바 라이브러리에는 CopyOnWriteArrayList 라는 병행성 컬렉션이 예외나 교착상태와 같은 문제를 쉽게 해결할 수 있게 하기위해 만들어졌다.


CopyOnWriteArrayList와 ArrayList는 모두 동일한데 한가지가 다르다. 어디론가 전달할 때 복사본을 전달한다는 것이다. 그렇게 때문에 전달 후에 해당 리스트의 내용이 변경될 것을 걱정하지 않아도 된다. 



클래스 내부적으로 동기화 메커니즘을 적용해야 한다면,

락 분할( Lock Splitting)

락 스트라이핑( Lock Striping)

비동기 병행성 제어( non-blocking concurrency control)

(https://stackoverflow.com/a/2824338/6602341)


와 같은 높은 병행성을 돕는 기법들을 활용할 수 있다.

















Comments