Java MultiThread(멀티스레드) 고려사항

1. Multi Thread 고려사항

MultiThread를 구현하는 대표적인방법은 두가지가 있습니다.

  • java.lang.Thread 클래스를 상속받아서 구현하는 방법
  • java.lang.Runnable 인터페이스를 구현해서 개발하는 방법

Field Member

필드의 의미는 클래스에 변수를 정의하는 공간을 의미합니다. 하지만, 객체가 여러 스레드가 접근하는 싱글톤 객체라면 filed 상태값을 갖고 있지않고, 모든 변수를 parameter -> return 형식으로 구성해야합니다.

Synchronized

동기화 처리시에 synchronized 키워드를 사용하여 스레드 간섭을 피하고 이 키워드 기반으로 구현된 Collection도 존재합니다.
List 대신 Vector, Map 대신 HashTable을 사용할 수 있지만, API 및 성능상 문제를 가지고 있습니다.

성능문제를 어떻게 해결 할 수 있을까요?

Collections 라는 utll 클래스에서 제공되는 static 메소드를 통해 해결이 가능합니다.

다음과 같이 변경하여 해결이 가능합니다.

1
2
3
Collections.synchronizedList()
Collections.synchronizedSet()
Collections.synchronizedMap()

더 나아가서, JDK 1.7부터는 ConcurrentHashMap 구현체를 제공하여 Collections utill을 사용하는것보다 보다 좋은 성능을 내는 자료구조를 제공합니다.

  • ConcurrentHashMap 예시
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
private final ConcurrentHashMap<String, Movie.MovieDto> movieMapCache = new ConcurrentHashMap<>(16);
private volatile Long cacheLoadTime = 0L;
private volatile Long cacheTimeLimit = 600 * 1000L;
* 영화 캐시 조회
* @param query
* @return
*/
public Map<String, Movie.MovieDto> findCacheByQuery(final String query) {
long nowTime = System.currentTimeMillis();
if(CollectionUtils.isEmpty(movieMapCache) || nowTime - cacheLoadTime > cacheTimeLimit){
synchronized (movieMapCache){
ResponseEntity<Movie.MovieDto> exchange = getExchange(query);
List<Movie.Item> ResponseMovieList = Movie.Item.of(exchange);
MovieGroup items = new MovieGroup(ResponseMovieList);
List<Movie.Item> listOrderRating = items.getListOrderRating();
Movie.MovieDto resultList = Movie.MovieDto.of(exchange, listOrderRating);
cacheInitialized();
cacheLoadTime = nowTime;
movieMapCache.put(query, resultList);
}
}
return movieMapCache;
}

ThreadLocal

일반 변수의 수명은 특정 코드 블록(예, 메서드 범위, for 블록 범위 등) 범위 내에서만 유효합니다. 하지만 ThreadLocal을 사용하면 반면에 ThreadLocal을 이용하면 쓰레드 영역에 변수를 설정할 수 있기 때문에, 특정 쓰레드가 실행하는 모든 코드에서 그 쓰레드에 설정된 변수 값을 사용할 수 있습니다. Thread사이에 간섭을 없애야하는 데이터에 사용이 되야하는데 멀티 스레드 환경에서는 클래스의필드에 멤버를 추가할 수 없고 매개변수로 넘겨받아야합니다. 결론적으로 스레드 내부의 싱글톤을 사용하기위해 사용합니다.

  • ThreadLocal 주의 사항
  1. ThreadLocal 객체를 생성한다.
  2. ThreadLocal.set() 메서드를 이용해서 현재 쓰레드의 로컬 변수에 값을 저장한다.
  3. ThreadLocal.get() 메서드를 이용해서 현재 쓰레드의 로컬 변수 값을 읽어온다.
  4. ThreadLocal.remove() 메서드를 이용해서 현재 쓰레드의 로컬 변수 값을 삭제한다.

쓰레드 풀 환경에서 ThreadLocal을 사용하는 경우 ThreadLocal 변수에 보관된 데이터의 사용이 끝나면 반드시 해당 데이터를 삭제해야하며 그렇지 않을 경우 재사용되는 쓰레드가 올바르지 않은 데이터를 참조할 수 있는 가능성이 있습니다.

  • ThreadLocal 사용
  1. 사용자 인증정보 전파 - Spring Security에서는 ThreadLocal을 이용해서 사용자 인증 정보를 전달합니다.
  2. 트랜잭션 컨텍스트 - 트랜잭션 매니저는 트랜잭션 컨텍스트를 전파하는 데 ThreadLocal을 사용합니다.
  3. 세션 정보
  4. Thread Safe한 데이터 저장