클래스가 스레드 안정성을 확보하도록 설계하고자 할 때는 아래 세 가지를 고려 해야 함
- 객체의 상태를 보관하는 변수가 어떤 것인가?
- 객체의 상태를 보관하는 변수가 가질 수 있는 값이 어떤 종류, 어떤 범위에 해당하는가?
- 객체 내부의 값을 동시에 사용하고자 할 때, 그 과정을 관리할 수 있는 정책
객체 내부의 변수가 모두 기본 변수형으로 만들어져 있다면 해당 변수만으로 객체의 상태를 완전하게 표현할 수 있다.
n개의 변수를 갖는 객체의 상태는 n개의 변수가 가질 수 있는 값의 전체 조합
스레드 안전한 클래스 설계
동기화 정책
- 객체 내부의 여러 변수가 갖고 있는 현재 상태를 사용하고자 할 때 값이 계속해서 변하는 상황에서도 값을 안전하게 사용할 수 있도록 조절하는 방법
- 객체의 불변성, 스레드 한정, 락 등을 어떻게 적절하게 활용해 스레드 안정성을 확보할 수 있으며 어떤 변수를 어떤 락으로 막아야 하는지 등의 내용을 명시함
상태 범위
- 객체와 변수를 놓고 보면 항상 객체와 변수가 가질 수 있는 가능한 값의 범위를 생각할 수 있음
- 상태 범위가 좁으면 좁을수록 객체의 논리적인 상태를 파악하기 쉬움
상태 의존 연산
- 현재 조건에 따라 동작 여부가 결정되는 연산
- 어떤 동작을 실행하기 전에 특정한 조건을 만족할 때까지 기다리도록 프로그램하고자 한다면, wait과 notify를 사용하는 대신 세마포어나 블로킹 큐와 같이 현재 알려져 있는 여러 가지 라이브러리를 사용하는 편이 훨씬 낫다.
상태 소유권
- 캡슐화 정책은 내부에 객체와 함께 상태 정보를 숨기기 때문에 객체의 상태에 대한 소유권이 있다. 특정 변수에 소유권을 갖고 있기 때문에 특정 변수의 상태가 올바르게 유지되도록 조절하는 락 구조가 어떻게 움직이는지에 대해서도 소유권을 갖는다.
- 소유권이란 말은 통제권이라는 말과 비슷한 의미를 갖지만, 다특정 변수를 객체 외부로 공개하고 나면 해당 변수에 대한 통제권을 어느 정도 잃는다. 다시 말해 객체를 공개하면 그저 ‘공동 소유권’ 정도를 가질 뿐이다.
인스턴스 한정
스레드 한정 기법을 사용해 특정 스레드 내부에서만 사용하게 할 수도 있고, 해당 객체를 사용하고자 하는 부분에서 락을 사용해 동시 사용되는 경우를 막아줄 수도 있다.
객체를 적절하게 캡슐화하는 것으로도 스레드 안전성을 확보할 수 있는데, 이런 경우 흔히 ‘한정'이라고 단순하게 부르기도 하는 ‘인스턴스 한정'기법을 활용하는 셈이다.
객체는 특정 클래스 인스턴스에 한정시키거나(클래스 내부에 private으로 지정된 변수), 문법적으로 블록 내부에 한정시킬 수도 있고(블록 내부의 로컬 변수), 아니면 특정 스레드에 한정시킬 수도 있다(특정 스레드 내부에서는 이 메서드에서 저 메서드로 넘어다닐 수 있지만, 다른 스레드로는 넘겨주지 않는 객체).
기본적인 컬렉션 클래스인 ArrayList나 HashMap 같은 클래스는 스레드에 안전하지 않지만, 자바 플랫폼 라이브러리에는 이런 클래스를 멀티스레드 환경에서 안전하게 사용할 수 있도록 도와주는 Collections.synchronizedList와 같은 팩토리 메서드가 만들어져 있다.
- 팩토리 메서드는 컬렉션의 기본 클래스에 스레드 안전성을 확보하는 방법으로 대부분 데코레이터 패턴을 활용하며, 이런 팩토리 메서드의 결과로 만들어진 래퍼 클래스는 기본 클래스의 메서드를 호출하는 연동 역할만 하면서 그와 동시에 모든 메서드가 동기화되어 있다. 즉 래퍼 클래스를 거쳐야만 원래 컬렉션 클래스의 내용을 사용할 수 있기 때문에 래퍼 클래스는 스레드 안전성을 확보할 수 있다.
자바 모니터 패턴
자바 모니터 패턴에 따르는 객체는 변경 가능한 데이터를 모두 객체 내부에 숨긴 다음 객체의 암묵적인 락으로 데이터에 대한 동시 접근을 막음
자바 모니터 패턴은 자바에 들어있는 Vector, Hashtable등의 여러 가지 라이브러리 클래스에서도 널리 사용하고 있음
스레드 안전성 위임
- 합성 객체: 둘 이상의 객체를 조합해 사용하는 객체
- 불변 변수는 스레드 안전하고, 불변의 값은 언제든 공유하고 공개할 수 있으므로 객체의 인스턴스를 복사해 줄 필요가 없다. 불변 변수를 멤버로 가지는 클래스는 불변 변수에 스레드 안전성 문제를 위임한다고 할 수 있다.
- 위임하고자 하는 내부 변수가 두 개 이상이라 해도 두 개 이상의 변수가 서로 ‘독립적'이라면 클래스의 스레드 안전성을 위임할 수 있는데, 독립적이라는 의미는 변수가 서로의 상태 값에 대한 연관성이 없다는 말
- 각각의 변수가 모두 스레드 안전한 클래스라고 하더라도 전체적으로는 스레드 안전성을 잃을 수 있음, 내부 변수끼리의 의존성이 있기 때문에 해당 클래스는 내부 변수가 스레드 안전성을 갖는다고 해서 단순히 안전성을 위임할 수는 없음
- 상태 변수가 스레드 안전하고, 클래스 내부에서 상태 변수의 값에 대한 의존성을 갖고 있지 않고, 상태 변수에 대한 어떤 연산을 수행하더라도 잘못된 상태에 이를 가능성이 없다면 해당 변수는 외부에 공개해도 안전하다.