
CPU, Java Thread에 대해 학습힌다.
Process와 Thread
1. Process란 ?
프로세스는 메모리에 올려져 실행중인 프로그램을 의미한다. 각 프로세스는 독립적인 메모리 공간과 실행 환경을 가지고 있다.
2. Thread란 ?
스레드는 CPU에서 실행되는 단위를 의미한다. 하나의 프로세스에 여러 스레드가 존재할 수 있고 같은 힙메모리를 공유한다.
Java Thread
1. Java에서 Thread란 ?
스레드는 CPU에서 실행되는 단위를 의미하고 JVM에 의해 관리 된다. 하나의 프로세스에 여러 스레드가 존재할 수 있고 같은 힙메모리를 공유한다.
2. Single Thread vs Multi Thread
Java 애플리케이션은 한개의 메인 스레드를 가지고 있고, 메인 스레드가 main() 메소드를 호출함과 동시에 실행되게 된다. 단일 스레드이기 때문에 한번에 한개의 일밖에 처리하지 못한다. 스레드를 추가로 만들어 주게 된다면 한 프로세스에서 그 이상의 일을 동시에 수행 가능해진다. 싱글스레드는 메인 스레드가 종료되면 프로세스도 종료되지만 멀티스레드는 실행중인 스레드가 하나라도 있다면 프로세스는 종료되지 않는다.
3. Thread 생성 방법
Thread 클래스를 상속 받아서 생성
public class ThreadSample extends Thread { /../ }
Runnable 인터페이스를 사용해서 생성
public class RunnableSample implements Runnable { /../ }
기존엔 Thread 클래스를 직접 상속 받아서 스레드를 생성했지만 스레드 클래스를 상속 받게 된다면 다른 클래스를 상속 받을 수 없다는 불편함이 있다. 이 문제를 해결하기 위해 대부분 Runnable 인터페이스를 상속 받아 스레드를 생성 해준다.
Thread Pool
1. Thread Pool이란 ?
스레드를 무한으로 늘리거나 스레드를 생성, 할당 해제 하는 과정에서 문제가 발생할 수가 있다(컨텍스트 스위치). 이런 문제를 해결하기 위해 스레드를 미리 만들어 놓고 재사용하는 방식인 스레드 풀 방식을 사용하게 된다.

제한된 스레드를 미리 생성 후, 큐에 들어오는 작업을 하나씩 스레드가 맡아 처리하는 기법을 스레드 풀이라고 한다. 작업량이 많거나 급격하게 늘어나도 큐에 대기 후 할당 작업이 끝난 스레드가 큐에서 들어오는 작업을 하나씩 처리하기 때문에 성능에 영향을 주지 않는 장점이 있다.
2. Thread Pool 사용시 주위 할 점
// pool 생성
ExecutorService pool =
Executors.newFixedThreadPool(
Runtime.getRuntime().availableProcessors()
);
// pool에서 필요한 작업 진행
for (int i = 0; i < 1000; i++) {
pool.submit(() -> doWork());
}
// 명시적 pool 종료
pool.shutdown();
스레드 풀은 한번 생성하게 된다면 shutdown()을 명시적으로 해주지 않으면 계속 살아있게 된다. 때문에 스레드 풀을 사용할땐 기능 단위가 아닌 애플리케이션 범위로 생각하고 만들어줘야한다. 잦은 생성과 소멸은 오히려 성능에 악영향이 되기 때문에 주의가 필요하다.
- 스레드 풀 생성
- 재사용
- 프로세스 종료 시 소멸
3. Android Thread Pool
안드로이드는 직접 스레드 풀을 사용하지 않아도 내부적으로 스레드풀을 사용하고 있다. 때문에 이미 스레드 풀 위에서 앱이 동작하고 있다.
AsyncTask (deprecated)- 안드로이드에서 스레트 풀을 제공한다는 첫 사례이다.
- 스레드 풀 크기 예측 어려움, 라이프사이클 관리 불가 등등 문제로 지금은 사용하지 않는다.
- Executor / ExecutorSerivce
- JVM 기반이기 때문에 java.util.concurrent 패키지의 스레드 풀 사용이 가능하다.
- OkHttp / Retrofit
- Dispatcher 스레드 풀을 사용한다.
- 내부적으로 ThreadPoolExecutor 객체를 사용하기 때문에 네트워크 통신을 할때 별도로 스레드 관리를 하지 않아도 된다.
- Room
- Room은 메인 스레드에 사용이 불가능하다.
- 내부적으로 ArchTaskExcutor 클래스를 사용한다.
- WorkoutManager
- Exeutor 기반이다.
- 스레드 풀 + JobScheduler + AlarmManager 조합이다.
- Coroutine Dispatcher
CPU, Thread
1. CPU에서 Thread가 동작하는 방식
CPU는 1개의 코어에서 1개의 스레드를 실행할 수 있다. 예로 코어가 1개인 경우 3개의 애플리케이션이 실행중이고 그 중 하나가 멀티스레드로 3개의 스레드를 가지고 동작하게 된다면 1개의 코어가 빠르게 컨텍스트 스위치를 하면서 5개의 스레드를 수행한다. 컨텍스트 스위치가 발생하는 속도가 매우 빠르기 때문에 사용자 입장에선 동시에 수행되는 것 처럼 느껴진게 된다.
동시에 여러 일을 하는 것 처럼 보이고, I/O 수행에서 효율을 높여주지만 스레드를 과도하게 많이 생성하게 되는 경우엔 오히려 CPU가 실제로 할당 업무를 수행하는 비용보다 더 큰 컨텍스트 스위치 비용이 요구 될 수가 있다. 때문에 무조건 많은 스레드를 생성하는 것보단 적절하게 필요한 만큼 생성하는 것이 중요하다.