쓰레드를 사용하게 될 경우 항상 발생되는 문제로 동시성 문제가 있습니다. 쓰레드의 경우 프로세스의 자원을 받아 하나의 로직을 동시에 처리할 수 있는 메커니즘을 가지고 있습니다. 이때 쓰레드는 파라미터나 메서드 내 사용되는 변수와 같은 스택 메모리는 각각 쓰레드 별로 할당되어 가지고 있지만 정적 변수가 저장되는 데이터 영역에는 여러 쓰레드가 동시에 접근할 수 있는 문제점이 있습니다.
그리고 스프링에서 controller는 싱글턴으로 만들지 않아 문제가 없지만 service의 경우 싱글턴 Bean으로 만들기 때문에 다른 스레드에서 해당 service가 가지고 있는 데이터를 접근할 수 있고 다른 서비스에서 변경할 수 있기에 예기치 못한 문제가 생깁니다.
예를들어 service의 필드 데이터에 여러 쓰레드가 동시에 접근하게 되어 데이터를 변경하게 되는 경우 필드 데이터의 결과값을 가져왔을 때 예상치 못한 값을 가져올 수 있기에 항상 주의깊게 다루어야 합니다. 이를 위해 쓰레드 로컬 저장소(TLS: Thread Local Storage)를 사용하게 되면 정적 메모리 영역에 접근 할 수 있는 TLS가 쓰레드별로 생성되기 때문에 race condition상태가 될 수 없습니다.

자바에서 Thread Local 저장소에 대한 생성은 아래와 같습니다.
private static final ThreadLocal<Integer> myThreadLocalInteger = new ThreadLocal<Integer>();
위와 같이 ThreadLocal을 생성하게 되면 각각의 스레드와 TLS가 연결된 객체가 생성됩니다.
쓰레드 로컬의 경우 각 쓰레드 별로 쓰레드 로컬 저장소를 가지고 있기 때문에 만약 쓰레드 로컬 저장소의 메모리 공간을 비워주어야 합니다. 만약에 WAS(톰캣)에서 쓰레드 풀을 사용하게 되는 경우 메모리 해제를 하지 않게 되면 하나의 쓰레드가 생성 후 데이터를 가져올 때 전 TLS에 저장된 값을 불러오는 결과가 발생됩니다.
따라서 Thread Local객체를 사용하면 반드시 remove메서드를 호출하여 TLS저장소를 해제해줘야 합니다.
물론 쓰레드 로컬을 사용해서 동시성 문제를 해결할 수 있지만, service layer에 POJO와 같은 JAVA코드를 필드로 생성하지 않는 게 좋아보입니다. 그 이유는 TLS 메모리를 해제 해야 하는 코드를 반드시 삽입해야 되고, 사용하지 않더라도 service 메서드에 파라미터로 전달되면 race condition같은 문제도 생기지 않습니다. 따라서 TLS를 사용하는 것보다 파라미터로 전달하여 동시성 문제를 해결하는 편이 좋습니다.