SynchronousQueue로 가변 Pool 구현하기

JDK 5까지 쓰레드 동기화 큐로 임무를 수행하던 LinkedBlockingQueue의 경우 동기화 대기가 상당한 부하를 주었습니다.
6.x에서 SynchronousQueue를 사용하면 단순히 takeput을 하는 경우 몇 배나 높은 성능을 기대할 수 있습니다. 이 구조가 다중 쓰레드 내에서 안전하면서 왜 빨라지는가는 이 포스트의 성격과 무관하므로 생략합니다만, 다음의 링크를 참조하면 도움이 됩니다.

http://suein1209.tistory.com/325

Pool에서 가져오기

쨌든 Pool을 구현하면 기본적으로 다중 쓰레드에 노출될 가능성을 염두 해야 하므로 가디언 패턴을 따라야 하지만, SynchronousQueue를 사용하면 간단히 정리할 수 있습니다.

우선 큐를 만들고 객체를 얻기 위한 GET함수를 작성해 보죠.

static SynchronousQueue<Sample> _pool = new SynchronousQueue<Sample>();

static String GET( final String $key ){
	Sample result = null;
	if( _pool.size() > 0 ){
		try{
			result = _pool.take().init( $key );
		}catch( InterruptedException $e ){}
	}else{
		result = new Sample().init( $key );
	}
	return result;
}

내용은 간단합니다. _pool에 뭔가 있으면 take로 가져오고 아니면 새로 만들게 됩니다.

만약 _pool.size() > 0 조건을 생략하면 GET함수를 호출한 쓰레드가 대기하게 되니 주의해야 합니다.

Pool에 반환하기

Sample 클래스를 간단히 살펴보죠.

class Sample{

	private String _key;

	public void init( String $key ){
		_key = $key;
	}

	public void run(){
		Log( _key );
		try{
			_pool.put( this );
		}catch( InterruptedException $e ){}
	}
}

여기서 핵심은 run에 있는 _pool.put 입니다. 실행을 완수하고 나면 다시 _pool로 돌아가는 것이죠.

결론

이러한 간단한 로직만으로 구현된 가변 Pool은 다음과 같은 상황에서 쓸모가 많습니다.

  • 여러 번 사용되지만 동시에 몇 개까지 사용될지 예측할 수 없다.
  • app의 생명주기 전반에 걸쳐 꾸준히 사용된다.
  • 즉시 지워야 할 정도로 큰 객체가 아니다.
  • 사용된 객체는 Pool로 돌아갈 타이밍이 정해져 있다.

위의 경우 가변 Pool을 사용하면 다음과 같은 장점이 생깁니다.

  • new를 최소화 한다.
  • 필요할 때 생성하므로 new를 지연한다.
  • 사용 후 재사용하게 되므로 GC에 부담을 줄인다.