객체 구성

스레드 안전한 클래스 설계

클래스가 스레드 안전성을 확보하도록 설계하고자 할 때 고려할것

객체 내부의 여러 변수가 갖고 있는 현재 상태를 사용하고자 할 때 값이 계속해서 변하는 상황에서도 안전하게 사용할 수 있도록 조절하는 방법을 동기화 정책이라고 한다. 동기화 정책에는 객체의 불변성, 스레드 한정, 락 등을 어떻게 적절하게 활용해 스레드 안전성을 확보할 수 있으며 어떤 락으로 막아야 하는지 등의 내용을 명시한다. 클래스를 유지보수하기 좋게 관리하려면 해당 객체에 대한 동기화 정책을 항상 문서로 작성해둬야 한다.

동기화 요구사항 정리

객체와 변수가 가질 수 있는 가능한 값의 범위를 상태 범위라고 한다. 상태 범위가 좁을수록 객체의 논리적인 상태를 파악하기 쉽다. final을 사용하면 상태 범위를 크게 줄여주기 때문에 생각해야 할 논리의 범위를 줄일 수 있다.

클래스 내부의 상태나 상태 변화에 관련해 제약 조건이 있을 수 있는데, 제약 조건에 따라 또 다른 동기화 기법이나 캡슐화 방법을 사용해야 할 수도 있다. 클래스가 특정 상태를 가질 수 없도록 구현해야 한다면, 해당 변수는 클래스 내부에 숨겨둬야만 한다. 특정 연산을 실행했을 때 올바르지 않은 상태 값을 가질 가능성이 있다면 해당 연산은 단일 연산으로 구현해야 한다. 상태 범위에 두 개 이상의 변수가 연결되어 동시에 관여하고 있다면 이런 변수를 사용하는 모든 부분에서 락을 사용해 동기화를 맞춰야 한다.

상태 의존 연산

현재 조건에 따라 동작 여부가 결정되는 연산을 상태 의존 연산이라고 한다.

여러 스레드가 동시에 움직이는 경우라면 실행하기 시작한 이후에 선행 조건이 올바른 상태로 바뀔 수도 있다. 병렬 프로그램을 작성할 때는 상태가 올바르게 바뀔 경우를 대비하고 기다리다가 실제 연산을 수행하는 방법도 생각할 수 있다.

자바에 내장된 wait와 notify 명령은 본질적으로 락을 사용하는 것과 굉장히 밀접한 관련이 있고, wait와 notify를 사용하면 특정 상태가 원하는 조건에 다다를 때까지 효율적으로 기다릴 수 있다.

하지만 사용하기 쉽지 않으니 wait와 notify를 사용하는 대신 세마포어나 블로킹 큐와 같이 현재 알려져 있는 라이브러리를 사용하는 편이 훨씬 간단하고 안전하다.

상태 소유권

변수를 통해 객체의 상태를 정의하고자 할 때에는 해당 객체가 실제로 ‘소유하는' 데이터만을 기준으로 삼아야한다. 예를 들어 HashMap 클래스 인스턴스를 하나 만들었다고 하면, HashMap 내부에서 기능을 구현하는 데 사용할 여러 개의 Map.Entry 객체와 기타 다양한 객체의 인스턴스가 만들어진다. 따라서 HashMap 객체의 상태와 기타 여러 가지 객체의 상태를 한꺼번에 다뤄야 한다.