[Java] - Thread, ThreadPool
최근에 재직했던 기업에서 ThreadPool을 이용해 외부 API 호출 개발경험이 있습니다.
기간이 조금 지나기도 했고 다시 공부하여 개념을 정리하기 위하여 포스팅을 작성해봅니다.
컴퓨터 프로그래밍와 OS에서 프로스세스 내에서 실행되는 하나의 작업단위를 의미합니다.
실행중인 하나의 애플리케이션을 프로세스라고 칭합니다.
사용자가 애플리케이션을 실행하면 운영체제로부터 실행에 필요한 메모리를 할당받아 애플리케이션의 코드를 실행하는데 이것이 프로세스입니다. (출처: 혼자공부하는 JAVA, 520P 스레드 파트)
Thread의 핵심 개념
- 스레드는 프로세스 안에서 실행되는 단위로써 여러 스레드가 한 프로세스 안에서 코드, 데이터, 파일 같은 자원을 공유하며 동작할 수 있습니다.
- 싱글 스레드 : 하나의 스레드로만 작업을 처리합니다. 단순하지만 작업이 많아질 경우 효율이 떨어질 수 있습니다.
- 멀티 스레드 : 하나의 프로세스 내에서 여러 스레드가 동시에 작업을 처리 합니다. 예를 들어, 웹브라우저는 한 스레드가 페이지를 로드하는 동안, 다른 스레드는 사용자 입력을 처리할 수 있습니다.
Thread의 특징
- 공유 자원 : 같은 프로세스 내에서 메모리와 자원을 공유합니다.
- 독립적인 실행 흐름: 각 스레드는 독립적인 실행 흐름을 가지며, 서로 다른작업을 수행합니다.
- 가벼움 : 프로세스를 생성하는 것보다 스레드를 생성하는 것이 더 가볍고 빠릅니다.
위와 같이 멀티스레드를 사용하면 '외부API 호출시 최소 1개 최대 10개의 API를 동시적으로 호출해야하는 동작'을 효율적으로 사용할 수 있겠다는 판단이들었고 멀티스레드를 활용하기 위해 ThreadPool을 사용하였습니다.
Thread 사용/미사용시 코드 실행흐름 예상도
1. Thread 미사용시 (순차적 진행)
2. Thread 사용시 (병렬 진행)
그리고 이 Thread를 좀 더 유용하게 쓰고자 ThreadPool을 사용했습니다.
ThreadPool이란?
스레드를 미리 생성하여 관리하는 스레드의 모음입니다.
이미 생성된 스레드 중 하나를 재활용하는 구조로써 성능향상과 효율적 자원관리를 도모할 수 있습니다.
스레드 생성 비용 감소, 과도한 스레드 생성방지, 효율적인 작원 활용의 장점이 있습니다.
ThreadPoolExecutor
그리고 ThreadPool을 사용하기 위해서 ThreadPoolExecutor클래스를 사용하여 실습을 진행했습니다.
(ThreadPoolExecutor클래스는 ExecutorService의 상속을 받으며 해당클래스는 포스팅 이후에 다시 공부해보려 합니다.)
위의 코드로 ThreadPool을 생성할 수 있습니다.
다만 생성자에는 5가지가 파리미터가 들어가게 되며 간략한 설명은 다음과 같습니다.
ThreadPoolExecutor 생성자 파라미터
- corePoolSize: Pool에 유지할 기본적인 스레드의 수
- maximumPoolSize: Pool에 허용된 최대 스레드의 수
- keepAliveTime: 유휴 상태의 스레드가 풀에서 유지될 최대시간
- workeQueue: 스레드 동작 전 작업대기 Queue
ThreadPoolExecutor 코드 실습
상황 : 6개의 Task(단순 6번 반복문)를 실행도록하여 Sysout을 통해 Thread와 Queue의 수를 확인해봤습니다.
콘솔창을 확인해보니 5개의 스레드가 생성되어 동작한것을 알 수 있었습니다. 어떻게 이런결과가 나왔을까요?
ThreadPoolExecutor 동작 방식
- ThreadPool생성시 workQueue의 사이즈를 1로 할당하여 해당 사이즈만큼의 스레드 대기
- 기본 ThreadPool의 사이즈(corePool)은 1로 할당. 하지만 의도했던 6개의 Task에서 1.의 workQueue에 대기중인 1개의 Thread를 제외한 크기는 5. ( Task(6) - WorkQueue(1) = 5)
- maximumPool로 가질수 있는 Thread의 크기 또한 5 이기 때문에 5개의 Thread가 ThreadPool로 생성되어 동작
- 5개 스레드 동작 + 1개의 스레드 동작 => 총 6개의 Task 수행 완료
위의 원리대로 Thread가 생성되고 동작된것입니다. 그리고 직접 도식화를 아래와 같이 해보았습니다.
위에 미처 공부하지 못한 ExecutorService와 ThreadPool을 통해 Return값을 활용할 수 있는 Future타입에 관한 것들은 추후에 공부하여 포스팅 할 예정입니다.