[Java][Thread Pool] 쓰레드 풀(Thread Pool) #1 쓰레드 풀이란 무엇인가?

2022. 2. 9. 19:09JAVA/Overview

학습목표
1. Thread Pool이 무엇인지 학습
2. 쓰레드풀 생성 및 실행 예제 학습

 

1. 쓰레드 풀(Thread Pool)가 필요하게 된 배경

데이터베이스나 웹 서버와 같은 서버 프로그램은 반복적으로 여러 클라이언트로부터 요청을 받아서 요청에 맞는 서비스를 제공합니다. 예를 들어 웹 서버는 클라이언트로부터 요청을 받으면 쓰레드를 새롭게 생성하여 서비스를 제공합니다. 하지만 문제점은 클라이언트로부터 요청이 올때마다 새롭게 쓰레드를 생성하고 해제하는 것은 많은 시간과 시스템 자원을 소모합니다.

 

활성화된 쓰레드는 시스템 자원을 소모하기 대문에 JVM은 너무 많은 쓰레드를 생성하게 되고 메모리 부족을 초래할 것입니다.

 

따라서 위와 같이 너무 많은 쓰레드 생성 문제를 해결하기 위해서 쓰레드 풀 방법이 존재합니다.

 

2. Thread Pool이란 무엇인가?

쓰레드 풀은 이전에 만든 쓰레드를 재사용하여 현재 작업을 실행하고 쓰레드 주기 오버헤드 및 자원 부족 문제에 대한 해결책을 제공합니다. 클라이언트 요청이 도착하였을때 이미 쓰레드는 존재하기 때문에 쓰레드 생성에 의한 지연은 제거되고 응용 프로그램은 더욱 반응적으로 설계할 수 있습니다.

 

즉, 쓰레드 풀은 미리 정해진 개수의 쓰레드를 생성해두고 서비스가 올때마다 풀 공간에 할일이 없는 쓰레드를 사용하여 서비스를 제공하는 방법입니다. 만약 생성해둔 쓰레드가 5개인데 6개의 요청이 한번에 온다면 남은 1개의 요청은 다른 쓰레드가 서비스를 마칠 때까지 대기하여야 합니다.

자바에서는 Executor 인터페이스를 중심으로한 Executor 프레임워크를 제공합니다.

Executor 인터페이스의 서브 인터페이스 또는 클래스

  • ExecutorService 인터페이스
  • ThreadPoolExecutor 클래스

Executor Thread Pool 메서드들

메서드명 설명
newFixedThreadPool(int) 고정된 사이즈의 쓰레드 풀 생성
newCachedThreadPool() 새로운 쓰레드가 필요한만큼만 동적 생성함
newSingleThreadExecutor() 싱글 쓰레드 생성

 

3. Thread Pool 예제

FixedThreadPool이라고 가정하고 다음과 같은 과정을 수행합니다.

  1. 실행할 테스크 생성(Runnable Object)
  2. 쓰레드 풀 생성
  3. 쓰레드 풀에 테스크 전달
  4. 테스크를 전부 마친 쓰레드 풀은 종료(shutdown)
public class Task implements Runnable{
	private String name;
    
    public Task(String s)
    {
        name = s;
    }
      
    // Prints task name and sleeps for 1s
    // This Whole process is repeated 5 times
    @Override
    public void run()
    {
        try
        {
            for (int i = 0; i<=5; i++)
            {
                if (i==0)
                {
                    Date d = new Date();
                    SimpleDateFormat ft = new SimpleDateFormat("hh:mm:ss");
                    System.out.println("Initialization Time for"
                            + " task name - "+ name +" = " +ft.format(d));   
                    //prints the initialization time for every task 
                }
                else
                {
                    Date d = new Date();
                    SimpleDateFormat ft = new SimpleDateFormat("hh:mm:ss");
                    System.out.println("Executing Time for task name - "+
                            name +" = " +ft.format(d));   
                    // prints the execution time for every task 
                }
                Thread.sleep(1000);
            }
            System.out.println(name+" complete");
        }
          
        catch(InterruptedException e)
        {
            e.printStackTrace();
        }
    }

}
public class Driver {
	static final int MAX_T = 3;
	
	public static void main(String[] args)
	{
        // creates five tasks
        Runnable r1 = new Task("task 1");
        Runnable r2 = new Task("task 2");
        Runnable r3 = new Task("task 3");
        Runnable r4 = new Task("task 4");
        Runnable r5 = new Task("task 5");      
          
        // creates a thread pool with MAX_T no. of 
        // threads as the fixed pool size(Step 2)
        ExecutorService pool = Executors.newFixedThreadPool(MAX_T);  
         
        // passes the Task objects to the pool to execute (Step 3)
        pool.execute(r1);
        pool.execute(r2);
        pool.execute(r3);
        pool.execute(r4);
        pool.execute(r5); 
          
        // pool shutdown ( Step 4)
        pool.shutdown();
	}
}
Initialization Time for task name - task 3 = 07:03:32
Initialization Time for task name - task 1 = 07:03:32
Initialization Time for task name - task 2 = 07:03:32
Executing Time for task name - task 3 = 07:03:33
Executing Time for task name - task 1 = 07:03:33
Executing Time for task name - task 2 = 07:03:33
Executing Time for task name - task 3 = 07:03:34
Executing Time for task name - task 2 = 07:03:34
Executing Time for task name - task 1 = 07:03:34
Executing Time for task name - task 3 = 07:03:35
Executing Time for task name - task 2 = 07:03:35
Executing Time for task name - task 1 = 07:03:35
Executing Time for task name - task 3 = 07:03:36
Executing Time for task name - task 2 = 07:03:36
Executing Time for task name - task 1 = 07:03:36
Executing Time for task name - task 3 = 07:03:37
Executing Time for task name - task 1 = 07:03:37
Executing Time for task name - task 2 = 07:03:37
task 3 complete
Initialization Time for task name - task 4 = 07:03:38
task 2 complete
task 1 complete
Initialization Time for task name - task 5 = 07:03:38
Executing Time for task name - task 4 = 07:03:39
Executing Time for task name - task 5 = 07:03:39
Executing Time for task name - task 4 = 07:03:40
Executing Time for task name - task 5 = 07:03:40
Executing Time for task name - task 4 = 07:03:41
Executing Time for task name - task 5 = 07:03:41
Executing Time for task name - task 4 = 07:03:42
Executing Time for task name - task 5 = 07:03:42
Executing Time for task name - task 4 = 07:03:43
Executing Time for task name - task 5 = 07:03:43
task 4 complete
task 5 complete

쓰레드풀의 최대 쓰레드 개수는 3개이고 테스크는 5개이므로 쓰레드 3개가 테스크 3개를 맡아 수행하고 나머지 2개의 테스크는 먼저 수행을 마친 쓰레드들이 맡아서 실행하는 것을 볼 수 있습니다.

 

References

Source Code : https://github.com/yonghwankim-dev/java_study/tree/main/threadpool/threadpool_01_basic
Geeks For Geeks : https://www.geeksforgeeks.org/thread-pools-java/?ref=gcse