ThreadPoolExecutor
* 항상 ThreadPool에 대해 관심은 있었으나
뭔가 복잡할거 같고 어렵고 접근하기 힘들어 그냥 Thread로만 대충 사용하다 보니 Multi Thread 환경에서 대략 난감인 경우들이 좀 있다.
그래서 찾아 보니 .ThreadPoolExecutor 라는 놈을 발견하게 되었다.
이것의 사용법을 함 알아보자.
ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue <Runnable > workQueue){
}
- corePoolSize : 실행할 최소 Thread수.
- maximumPoolSize : 최대 Thread 지원수
- keepAliveTime : 현재 풀에 corePoolSize 의 수보다 많은 thread가 있는 경우, 초과한 만큼의 thread는, IDLE 상태가 되어 있는 기간이 keepAliveTime 를 넘으면(자) 종료합니다
- unit : 시간단위.
- workQueue : 처리 큐
( Java Doc 참고 : http://xrath.com/javase/ko/6/docs/ko/api/java/util/concurrent/ThreadPoolExecutor.html )
여기서 중요한것은 큐입니다.
JavaDoc을 보면.BlockingQueue 에 적용가능한 Queue는 다음과 같습니다.
SynchronousQueue
- 워크 큐에 적절한 디폴트의 선택사항은, 태스크를 보관 유지하지 않고 thread에 핸드 오프.
- 일반적으로 직접 핸드 오프에서는, 송신된 새로운 태스크가 거부되는 것을 회피하기 위해서, 안 바운드 형식의 maximumPoolSizes 가 필요합니다.
- 이것에 의해, 평균해 처리 능력을 넘는 속도로 커멘드가 차례차례로 도착하면(자), 안 바운드 형식의 thread가 커질 가능성이 있습니다.
ThreadPoolExecutor threadPool = new ThreadPoolExecutor( 2, 4, 60, TimeUnit.SECONDS, new SynchronousQueue <Runnable>() );
for( int i = 0; i < 6; i++ ) {
threadPool.execute( new Runnable() {
public void run() {
System.out.println( "Thread " + Thread.currentThread().getName() + " Start" );
try {
Thread.sleep( 1000 );
} catch( Exception e ) {
}
System.out.println( "Thread " + Thread.currentThread().getName() + " End" );
}
} );
}
- 2개의 Thread사용
- 최대 4개 Thread사용 ( SynchronousQueue 사용시에는 maximumPoolSizes(최대 Thread개수) 값이 필요)
- 60초의 Timeout 설정
- 큐는 SynchronousQueue
- 이렇게 하면 최대 Thread개수인 4개를 모두 사용하게 됨.
- for문을 통해 Thread 실행시 6개의 Thread가 실행되는데 최대 사용가능한 Thread 개수는 4개 이므로 실행중
RejectedExecutionException() 발생함.
( SynchronousQueue 의 경우는 Task를 보관하지 않고 바로 실행하므로 최대 개수 4를 넘는 Thread 실행시 Exeption 발생함. )
LinkedBlockingQueue
- corePoolSize 의 모든 thread가 Busy 상태인 경우에, 새로운 태스크는 큐내에서 대기합니다. 이것에 의해, corePoolSize 를 넘는 thread는 작성되지 않게 됩니다.
즉 maximumPoolSize 의 값은 효과가 없어집니다. (maximumPoolSize 값은 의미 없음) - 각 태스크가 완전하게 독립하고 있기 (위해)때문에, 태스크가 상호의 실행에 영향을 주지 않는 경우는 이 방식이 적절하다라고 하는 것이 있습니다 (Web 페이지 서버의 경우 등).
- 이 방식의 큐잉은, 일시적으로 급증한 요구를 처리하는 경우 등은 편리합니다만, 평균 해 처리 능력을 넘는 속도로 커멘드가 차례차례로 도착하면, 안 바운드 형식의 워크 큐가 커질 가능성이 있습니다.
ThreadPoolExecutor threadPool = new ThreadPoolExecutor( 2, 4, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>() );
for( int i = 0; i < 6; i++ ) {
threadPool.execute( new Runnable() {
public void run()
{
System.out.println( "Thread " + Thread.currentThread().getName() + " Start" );
try {
Thread.sleep( 1000 );
} catch ( Exception e ) {
}
System.out.println( "Thread " + Thread.currentThread().getName() + " End" );
}
} );
}
- 2개의 Thread사용
- 최대 4개 Thread사용 (LinkedBlockingQueue에서는 maximumPoolSize 값은 의미 없음 )
- 60초의 Timeout 설정
- 큐는 LinkedBlockingQueue
[ 실행 결과 ]
Thread pool-1-thread-1 Start
Thread pool-1-thread-2 Start
Thread pool-1-thread-1 End
Thread pool-1-thread-1 Start
Thread pool-1-thread-2 End
Thread pool-1-thread-2 Start
Thread pool-1-thread-1 End
Thread pool-1-thread-1 Start
Thread pool-1-thread-2 End
Thread pool-1-thread-2 Start
Thread pool-1-thread-1 End
Thread pool-1-thread-2 End
* 이렇게 하면 총 6개의 Thread가 돌지만. 2개의 Thread가 실행되고 나머지는 큐에 대기하고 있다가 2개의 Thread가 종료되면 이후 실행된다.
ArrayBlockingQueue
- 한정된 maximumPoolSizes 로 사용하면 자원 부족을 회피할 수 있습니다만, 조정과 제어가 어려워질 가능성이 있습니다. 큐 사이즈와 최대 풀 사이즈는 서로 트레이드 오프의 관계가 되는 일이 있습니다.
'안드로이드 > Thread' 카테고리의 다른 글
ExecutorService (0) | 2015.12.08 |
---|
댓글