인프런 김영한 강사님의 강의를 듣고 정리한 내용입니다.
하나의 데이터에 여러개의 쓰레드가 접근하여 값을 변경하게 되는 경우, 의도하지 않은대로 동작할 수 있다. 이를 동시성 문제라고 한다.(읽기만 할 때는 발생하지 않는다.)
동시성 문제는 지역변수
에서는 발생하지 않는다. 지역 변수는 쓰레드마다 각각 다른 메모리 영역이 할당된다.
그 이유는 쓰레드마다 각각의 스택
영역이 부여되기 때문이다. 스택에 메서드들이 호출되어 쌓이면, 메서드 내부의 지역 변수
들은 각각의 스택
공간에 할당된다.
하지만 인스턴스의 필드
는 힙
영역에 할당되고 모든 쓰레드는 같은 힙 공간
을 참조하게 된다. 그러므로 필드로 관리하는 경우에는 여러 쓰레드가 접근할 경우 동시성 문제가 발생한다.
동시성 문제가 발생하는 곳은 같은 인스턴스의 필드(주로 싱글톤에서 자주 발생), 또는 static 같은 공용 필드에 접근할 때 발생한다.
쓰레드 로컬은 해당 쓰레드만 접근할 수 있는 특별한 저장소를 말한다. 쓰레드 로컬을 사용하게 되면, 해당 쓰레드의 저장소에만 접근할 수 있으므로 다른 쓰레드의 저장소를 변경하는 일이 발생하지 않는다. 그러므로 동시성 문제를 해결할 수 있다.
쓰레드 로컬은 힙 영역의 쓰레드마다 분리된 공간
에 저장된다.
// private String nameStore; // 인스턴스의 필드
private ThreadLocal<String> nameStore = new ThreadLocal<>(); // 쓰레드 로컬
쓰레드 로컬 객체에 타입을 지정하여 쓰레드 로컬로 만들 수 있다.
값을 저장할 때는 set을 사용하고, 읽을 때는 get을 사용한다.
그리고 값 제거 시에는 remove를 사용한다.
WAS(톰캣)에서는 쓰레드를 사용하고 나서 해당 쓰레드는 종료되지 않고 쓰레드 풀에 반환된다. (쓰레드를 종료시키고 재생성하면 메모리를 할당받는 비용이 크게 들기 때문에 종료 대신 반환을 한다.) 반환된 쓰레드는 다른 클라이언트가 사용할 수 있다.
그런데 쓰레드로컬 메모리 공간에 이미 저장된 데이터가 있다면 잘못된 값을 읽게되는 문제가 발생할 수 있다. 그렇기 때문에 항상 쓰레드의 반환 이전에 쓰레드로컬의 remove를 통해서 쓰레드로컬의 데이터를 삭제해주어야 한다.