Thread Safe의 조건

쓰레드 개념이야 알고 있지만, 웹 개발을 하다보니 실제로 개발하는 코드가 쓰레드 세이프한지 별 고민없이 개발을 해왔던 것 같다. 쓰레드 세이프 조건에 대해서 알아보자.

하나의 쓰레드만 존재한다.

어플리케이션이 하나의 쓰레드로 동작한다면, 쓰레드간 간섭이 없으니 당연히 쓰레드 세이프하다.

인스턴스를 공유하지 않는다.

복수의 쓰레드가 동작하더라도 서로 공유하는 인스턴스가 없다면 쓰레드 세이프하다. 그리고 무상태 타입의 쓰레드라면 공유할 인스턴스 자체도 필요없어 이 역시 쓰레드 세이프하다. 또한, 상태를 담는 인스턴스를 갖더라도 ThreadLocal과 같은 쓰레드 별 공간이라면 이 조건을 만족하게 된다.

인스턴스가 속성을 갖지 않는다.

어떤 기능을 가진 인스턴스가 싱클톤으로 제공되어 모든 쓰레드가 동시에 사용을 할 때, 이 인스턴스가 속성을 갖지 않는다면 쓰레드 세이프하다. 인스턴스의 메소드가 호출 될 때, 메소드의 매개 변수나 로컬 변수는 그 메소드 호출 시, 각 쓰레드 별로 메모리가 할당되기 때문에 쓰레드간 간섭이 생길 수 없다. 그래서 쓰레드 세이프하다. Util성 클래스들이 이러한 예이다.

속성이 runtime 에 변경되지 않는다.

쓰레드간 공유되는 인스턴스가 속성을 같더라도 runtime 에 변경되지 않는다면 쓰레드 세이프하다. 쓰레드 사용의 근본적인 원인은 한 쓰레드가 속성 값을 설정하고 이용해야 하는데, 그 사이에 다른 쓰레드가 속성 값을 변경하는 문제이다. 그러나 이 속성 값을 상수로 선언하거나 초기화 후, 변경 불가능하게 해서 외부에서는 읽기만 가능하게 한다면 쓰레드 세이프하다.

속성에 동시접근이 불가능하다.

보통 synchronized 블럭을 사용하여 이 조건을 만족시킨다. 값을 설정 및 조회하는 동안 다른 쓰레드가 접근하지 못하게 하는 것이다. 하지만 synchronized 블럭에 접근할 수 있는 쓰레드는 단 하나이기 때문에 다른 쓰레드는 대기해야 하고, 이는 성능에 악영향을 끼친다.

클래스 자체가 쓰레드 세이프하다.

동시에 사용하는 인스턴스 자체가 쓰레드 세이프한 경우를 말한다. 쓰레드 세이프 여부를 javadoc이나 각 개발자의 주석을 통해서 확인하면 된다.

멀티 쓰레드 환경에서 스프링 빈

스프링 빈을 사용할 때, 각 빈은 기본적으로 싱글톤 방식의 오브젝트가 되고 이들의 멤버 변수 역시 스프링 빈이거나 상수이다. 하지만, 스프링 어플리케이션 컨텍스트 영역에 스프링 빈들이 등록되고 이 빈들은 멀티 쓰레드 환경에서 공유된다. 즉, 스프링 프레임워크가 쓰레드 세이프하다고 해도 로컬 변수가 쓰레드 세이프 한거지 멤버 변수까지 쓰레드 세이프하다는 말은 아니다. 그래서 스프링 빈에 멤버 변수는 쓰레드 세이프한지 아닌지 고민하면서 작성해야 한다.

Reference