관리 메뉴

HAMA 블로그

JAVA 쓰레드풀의 상태관리 본문

Java

JAVA 쓰레드풀의 상태관리

[하마] 이승현 (wowlsh93@gmail.com) 2018. 3. 8. 12:35



자바 쓰레드풀의 상태관리

쓰레드풀의 상태를 나타내는 멤버변수인 ctl 에 대해  (코딩 스킬적으로) 살펴보자.
*  코딩 스킬적으로 
 하나의 int 변수 안에  길이가 다른 다양한 특성을 담아둘때 이런 기법을 이용하시면 된다. 

private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0)); // RUNNING 상태와 쓰레드 개수1로 초기화
private static final int COUNT_BITS = Integer.SIZE - 3;
private static final int CAPACITY = (1 << COUNT_BITS) - 1; // 00011111 11111111 11111111 11111111

// runState is stored in the high-order bits
private static final int RUNNING = -1 << COUNT_BITS; // 11100000 00000000 00000000 00000000
private static final int SHUTDOWN = 0 << COUNT_BITS;
private static final int STOP = 1 << COUNT_BITS;
private static final int TIDYING = 2 << COUNT_BITS;
private static final int TERMINATED = 3 << COUNT_BITS;

private static int ctlOf(int rs, int wc) { return rs | wc; } // 2가지 상태를 하나의 Int 로 합쳐주는 함수

이 ctl 는 하나의 변수 안에 2가지 내용을 패킹시켜 두었다. 

첫째. workerCount   - 
현재  워커(쓰레드) 의 개수  (앞의 3비트 이용)
둘째. runState - 쓰레드풀의 현재 상태 (running, shutting down 등등)  (뒤의 29비트 이용) 

위의 코드를 보면 ctlOf 메소드를 통해서 초기에는 (RUNNING,0) 으로 초기화 시켜 놓은 부분이 보일 것이다. 즉 초기 상태는 
workerCount 는 0 이고 runState 는 RUNNING 이라는 것이다. 앞쪽비트는 runState 로 사용하고, 뒤쪽 비트는 workerCount 로 사용 한다는 것인데, 맞는지 확인 해보자.

- CAPACITY ( -1 을 해주는 이유는 하위 모든 비트를 1로 만들기 위해서이다. 대략 5억으로 workCount 의 용량제한이다.)
(1<< 29) - 1 = 2^29 -1  =  536870911 = 0x1FFFFFFF = 00011111 11111111 11111111 11111111
 

- RUNNING 은 다음과 같다.
-1 << 29  =  -536870912 =   0xE0000000 = 11100000 00000000 00000000 00000000

위 쪽의 3비트와 나머지 비트로 나누어 진 것을 알 수 있다. 

따라서 ctl 의 초기 상태는 다음과 같다.
11100000 00000000 00000000 00000000


workerCount
하나의 Int 타입으로 2가지 내용을 패킹하기 위해서 먼저 workerCount 를 대략 5억개로 제한시켜두었고 

private static final int COUNT_BITS = Integer.SIZE - 3; // 32-3 = 29

private static final int CAPACITY = (1 << COUNT_BITS) - 1; // 00011111 11111111 11111111 11111111

private static int workerCountOf(int c) { return c & CAPACITY; } // 현재 워커 갯수는?

비트마스킹이 조금이라도 더 빠르니깐 이렇게 한것이며, workerCount 는 워커 쓰레드의 숫자로써, 살아있는 쓰레드들의 실질적인 숫자로써 계속 달라 질 것이다.(예를들어 ThreadFactory 가 쓰레드 생성에 실패 했을 때, 종료되는 와중에도 여전히 태스크 처리를 수행 될 때 등등), 내부적으로 사용되는 workerCount와는 다르게 사용자에게 알려지는 풀의 크기는 worker 를 모아둔 workers(HashSet) 의 크기가 된다.  

현재 ctl 의 초기 상태는 다음과 같다. 이것이 workerCountOf 의 매개변수인 c 로 들어가게 되는데 

11100000 00000000 00000000 00000000   (ctl)
 00011111 11111111 11111111 11111111 (capacity)

이 둘을 비트연산(&)하면 0 이다. 맞는지 확인 해보자.
int c = ctl.get();   
int ws = workerCountOf(c); // ws 는 0

runState
RUNNING: 새로운 태스크를 받고,  큐에 집어 넣는 일을 하라.
SHOUTDOWN: 새로운 태스크를 받지마라, 그러나 이미 큐에 있는 태스크는 처리하라
STOP: 새로운 태스크를 받지마라, 큐에 있는 태스크도 처리하지 않는다. 현재 진행중인 태스크에 인터럽트를 건다.
TIDYING: 모든 태스크는 소멸되었고, workerCount 는 0 이다. TIDYING 상태로 전이되는 쓰레드는 terminated() 훅 메소드를 실행 시킬 것이다. 
TERMINATED: terminated() 메소드가 완료 되었다. 

전이형태는 다음과 같다.
RUNNING -> SHUTDOWN    ( shoutdown() 메소드 호출시)
(RUNNING or SHUTDOWN) -> STOP ( shoutdownNow() 메소드 호출시)
SHUTDOWN -> TIDYING ( 풀과 큐가 비었을 때)
STOP -> TIDYING (풀이 비었을 때) 
TYDYING -> TERMINATED (terminated() 메소드가 완료 되었을 때)


Comments