ThreadPoolExecutor
일반적으로 응용 프로그램은 전용 스레드 모델(dedicated thread model)을 이용합니다.
위 그림처럼 첫 번째 스레드는 오디오 스트리밍만을 처리하고, 두 번째 스레드는 데이터베이스 권한만 처리하고, 세 번째 스레드는 네트워크만 처리합니다.
이 구성은 스레드 당 작업량이 많지 않으므로, 순차적으로 처리하는 것이 적합합니다.
하지만 만약 스레드당 처리해야 할 작업량이 많아지면 순차적으로 처리하는 것이 적합하지 않습니다.
예를 들어, 디코딩할 40개의 비트맵이 있고, 각 디코딩은 4ms 정도 소요될 때, 모든 작업을 순차적으로 처리하려면 총 160ms [40 * 4ms]가 걸립니다.
따라서 모든 작업을 하나의 전용 스레드에 넣는 것은 좋지 않습니다.
반면에 10개의 스레드를 생성하고 각각 4개의 비트맵만 병렬로 디코딩하면 16ms[(40 / 10) * 4] 밖에 걸리지 않습니다.
처리 시간이 10배로 줄어들기 때문에 당장 좋아 보이지만, 스레드들 사이에 적절한 방법으로 작업을 전달하고, 우선순위도 지정해야 하고, 이를 관리해야 하는 문제도 발생합니다.
다행히 안드로이드는 ThreadPoolExecuter Helper 클래스를 제공하고 있습니다.
ThreadPoolExecutor는 스레드 그룹 생성을 관리하고 우선순위를 설정하며 해당 스레드 간에 작업이 분산되는 방식을 관리합니다.
코드상 원하는 만큼의 스레드를 생성할 수 있습니다.
하지만 CPU는 제한된 개수의 스레드 만 병렬로 실행할 수 있습니다.
만약 스레드가 제한된 개수보다 더 많아지게 된다면, 내부적으로 우선순위에 따라 스케줄링이 일어나고, 스레딩 성능 이슈가 발생할 수 있습니다.
즉, 스레드의 수가 계속 증가하더라도 작업 처리 속도는 그에 비례하여 빨라지지는 않습니다.
스레드 수를 결정할 때 메모리 문제도 고려해야 합니다.
각 스레드는 최소한 64K의 메모리를 필요로 하며, 특히 기기에 앱이 많이 설치 되어 있고 호출 스택(call stack)이 많아질 수 있는 상황에서는 메모리가 빠르게 증가합니다.
Systrace를 사용하면 Android 장치의 많은 실행 중인 시스템에 응용 프로그램 실행을 적용하는 방법을 분석할 수 있고, 이를 바탕으로 문제없이 실행될 수 있는 최소 스레드 수를 발견할 수 있습니다.
https://developer.android.com/studio/profile/systrace-commandline.html
https://developer.android.com/studio/profile/systrace.html
스레드 풀을 만들 때, 초기 스레드 수와 최대 스레드 수를 지정할 수 있습니다.
이 외에도 대기 시간, 작업 큐 등을 설정할 수 있습니다.
구현과 관련된 자세한 내용은 아래 링크들을 참조해 주시기 바랍니다.
http://128.199.231.48/threadpoolexecutor/
https://blog.mindorks.com/threadpoolexecutor-in-android-8e9d22330ee3#.mnsoheyvq
스레드 수 를 지정하기 위해 아래와 같이 사용 가능한 코어 개수를 얻어 오는데, 이때 반환되는 값이 장치의 물리적 코어 수를 반영하지 않을 수도 있습니다.
int NUMBER_OF_CORES = Runtime.getRuntime(). availableProcessors();
일부 기기에는 시스템 부하에 따라 하나 이상의 코어를 비활성화하는 CPU가 있기 때문에, 실제로 두 개의 CPU가 있지만 그중 하나가 잠들어 있으면 하나만을 반환할 수 있습니다.
스레드 풀을 지원해주니 좋네요.
하지만 초기 스레드 개수와 최대 개수 등 상황에 맞게 구현해야 하기 때문에 마냥 쉽지는 않네요.
https://developer.android.com/training/multiple-threads/create-threadpool.html
https://developer.android.com/reference/java/util/concurrent/ThreadPoolExecutor.html
https://www.youtube.com/watch?v=uCmHoEY1iTM&t=118s&index=6&list=PLWz5rJ2EKKc9CBxr3BVjPTPoDPLdPIFCE