스레드 풀 활용
작업과 실행 정책 간의 보이지 않는 연결 관계
일정한 조건을 갖춘 실행 정책이 필요한 작업
작업이 실행 정책에 의존성을 가진 경우가 있다.
의존성 있는 작업
스레드 한정 기법을 사용하는 작업
응답 시간이 민감한 작업
ThreadLocal을 사용하는 작업
스레드 풀은 서로 독립적인 다수의 작업을 실행할 때 좋다 크기가 제한된 스레드 풀에 의존성 있는 작업을 등록하면 deallock 작업은 실행 정책을 요구하거나, 특정 실행 정책 아래에서는 실행되지 않는 경우가 있다.
스레드 부족 데드락 (thread starvation deadlock)
스레드 풀에서 다른 작업에 의존성 가진 작업 실행 > 데드락 걸릴 가능성 ⤴️
스레드 부족 데드락
이전 작업에서 추가한 작업이 실행되어 결과를 알려주기 기다리는 상황이라면, 이전 작업이 스레드를 점유하고 있기 때문에 추가한 작업이 실행되지 않아서 데드락에 빠지게 된다. 스레드 풀을 사용해도 모든 작업이 아직 실행되지 않은 작업의 결과를 기다릴 때도 발생할 수 있다.
완전히 독립적이지 않은 작업을 Executor에 등록할 때는 항상 스레드 부족 데드락에 주의하자.
오래 실행되는 작업
스레드 풀 크기 조절
풀의 가장 이상적 크기는 다음에 따라 다르다.
스레드 풀의 설정을 하드코딩해 고정하는 건 그다지 좋은 방법이 아니다.
설정 파일이나, Runtime.availableProcessors등 메소드 결과에 따라 동적으로 지정하자.
작업의 속성에 따라 풀을 나눌 수 있다
스레드 풀 크기와 자원 풀 크기가 서로에게 영향을 미친다.
ThreadPoolExecutor 설정
ThreadPoolExecutor instance
스레드 생성과 제거
core, maximum, keep-alive를 설정할 수 있다.
core > 풀에 실행할 작업이 없더라도 코어 크기 만큼은 유지, 단 최초에 모든 스레드를 생성하는 게 아니라 core 까지는 요청 받고 생성
maximum > 작업 큐가 가득 차서 스레드를 추가할 때 최대 스레드 수
keep-alive > 작업 없이 이 시간이 지나고 스레드가 core 갯수보다 많다면 제거 대상
큐에 쌓인 작업 관리
스레드 풀에서 작업을 List<Runnable>로 관리하는 것이 매번 스레드를 만드는 것보다 자원을 덜 소모한다.
하지만 여전히 작업이 많이 들어올 때 시스템 자원이 모자란 것은 맞다. 처리하는 속도보다 빠른 속도록 작업이 추가될 때 속도 조절 기능을 사용해 메모리가 가득 차는 현상을 막아야 한다.
큐 크기에 제한을 두는 방식
크기 제한 ❌
크기 제한 ⭕️
큐에 안 쌓음
다른 작업에 의존성 갖는 작업 실행은 스레드 큐 크기 제한되면 안되므로 newCachedThreadPool 과 같은 스레드 풀 사용해야 한다.
집중 대응 정책 (saturation policy)
집중 대응 정책 (saturation policy) > 크기가 제한된 큐에 작업이 가득찰 때 스레드 풀의 대응 정책 RejectedExecutionHandler로 표현할 수 있다.
중단 정책 (abort policy) > 작업을 추가할 수 없음을 에러를 통해 알린다. ThreadPoolExecutor.AbortPolicy 로 표현
제거 정책 (discard policy) > 추가하려는 작업을 아무 반응 없이 제거한다. ThreadPoolExecutor.DiscardPolicy 로 표현
오래된 항목 제거 정책 (discard oldest policy) > 큐에서 실행 예정된 작업 제거하고 새로 받은 일 추가 ThreadPoolExecutor.DiscardOldestPolicy 로 표현 FIFO 큐인 경우에는 오래된 항목이 제거되고, Priority 큐인 경우에는 가장 우선순위 높은게 제거된다. 따라서 Priority 큐와는 궁합이 좋지 않다.
호출자 실행 정책 (caller runs policy) > 작업 프로듀서 스레드가 작업을 처리하도록 넘긴다. ThreadPoolExecutor.CallerRunsPolicy 로 표현 execute 호출자 스레드에서 실행한다는 의미 작업 추가 스레드에 처리를 넘기기 때문에 작업의 추가 속도를 늦출 수 있다.
부하가 점점 밖으로 드러나는 형태
웹 서버 내부 스레드 풀 부하 > 서버 동작하는 TCP 계층 부하 > 클라이언트가 느끼는 부하
스레드 팩토리
스레드 풀에서 새로운 스레드는 스레드 팩토리를 통해서 생성
E.g
ThreadPoolExecutor 생성 이후 설정 변경 > Executors.unconfigurableExecutorService(executor) 를 활용해 더이상 설정 변경 못하게 만들 수 있다.
ThreadPoolExecutor 상속 > beforeExecute, afterExecute는 같은 Thread에서 실행되기에 두 작업 사이에 값을 전달하고 싶으면 ThreadLocal을 활용할 수 있다.
재귀 함수 병렬화
다음 케이스가 모두 만족하는 경우 병렬화하기 좋은 대상
Last updated