일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 데이터베이스
- 자료구조
- 자바
- 파이썬
- 프로그래머스
- github
- DevOps
- 머신러닝
- ubuntu
- Kubernetes
- 우분투
- 쿠버네티스
- 코테
- LV 0
- Ai
- mysql
- Lv 2
- 정처기
- 인공지능
- programmers
- git
- 깃
- db
- 리눅스
- Linux
- Python
- 코딩테스트
- 알고리즘
- Java
- docker
- Today
- Total
Myo-Kyeong Tech Blog
[ JAVA ] 스레드 풀(Thread Pool) - Java로 Thread Pool의 구현 및 서버 애플리케이션 활용 본문
[ JAVA ] 스레드 풀(Thread Pool) - Java로 Thread Pool의 구현 및 서버 애플리케이션 활용
myo-kyeong 2023. 7. 16. 18:07
2023.07.16 - [Programming/Java] - [ JAVA ] 스레드 풀(Thread Pool) - Thread Pool을 사용해야 하는 이유
이전 글에서 Thread Pool이 왜 필요한지에 대해 설명했었는데요. 이번 글에서는 그 이유를 바탕으로 직접 Java 코드로 Thread Pool을 구현하고, 이를 활용하여 서버 애플리케이션을 만들어 보는 과정에 대해 살펴보겠습니다.
Thread Pool 구현하기
우선, Thread Pool과 각각의 스레드가 어떻게 동작해야 하는지에 대해 이해해야 합니다. 각각의 스레드는 요청이 들어올 때마다 생성되지 않고, Thread Pool에 의해 관리되며 재사용됩니다. 이를 위해 ManagedThread라는 이름의 클래스를 만들어, 각각의 스레드가 수행할 작업(Job)을 지정하고, 작업이 완료되면 Thread Pool로 스레드를 반환하는 기능을 구현합니다.
이러한 스레드 관리를 위해 Thread Pool을 구현할 때는, 처음에 스레드를 생성하고, 사용 후에는 반환하는 기능이 필요합니다. 이를 위해 'ResourcePool'라는 인터페이스를 정의하고, 이를 구현한 'ThreadPool' 클래스를 만들어서 사용합니다.
[ 클래스 및 인터페이스 정의 ]
- ManagedThread : 실행할 작업(Job)을 가지고 있는 스레드를 나타내는 클래스. 작업이 할당되면 스레드는 해당 작업을 수행
- Job : 수행할 작업을 나타내는 인터페이스
- ResourcePool : 스레드를 관리하는 풀의 인터페이스
- ThreadPool : ResourcePool 인터페이스를 구현하는 클래스로, 실제로 스레드를 생성, 재사용, 반환하는 역할
ManagedThread
ManagedThread는 ResourcePool의 리소스를 참조하며, 실제 작업(Job)을 수행하는 역할을 합니다. 작업(Job)이 설정되면, 이 작업을 수행한 후 다시 ThreadPool로 반환됩니다.
public class ManagedThread extends Thread {
static int count = 0;
int no;
ResourcePool<ManagedThread> pool;
Job job;
public ManagedThread(ResourcePool<ManagedThread> pool) {
this.pool = pool;
no = ++count;
}
public void setJob(Job job) {
this.job = job;
synchronized (this) {
this.notify();
}
}
@Override
synchronized public void run() {
while (true) {
try {
synchronized (this) {
this.wait();
}
System.out.printf("%d 번 스레드 실행!\n", no);
this.job.execute();
pool.returnResource(this);
} catch (Exception e) {
System.err.println("스레드 실행 중 오류 발생!");
e.printStackTrace();
}
}
}
}
Job 인터페이스
Job 인터페이스는 execute()라는 단일 메소드를 가지고 있습니다. 이 메소드를 통해 작업(Job)이 수행되며, 이를 ManagedThread에서 호출합니다.
public interface Job {
void execute();
}
ResourcePool 인터페이스
ResourcePool 인터페이스는 getResource()와 returnResource()라는 두 개의 메소드를 가집니다. 이 인터페이스를 통해 스레드 풀의 리소스를 관리할 수 있습니다.
public interface ResourcePool<T> {
T getResource();
void returnResource(T resource);
}
ThreadPool
마지막으로, ThreadPool 클래스는 위에서 설명한 ResourcePool 인터페이스를 구현합니다. 이 클래스는 스레드를 생성하고, 필요한 경우 기존 스레드를 재사용하며, 사용이 끝난 스레드를 반환합니다.
public class ThreadPool implements ResourcePool<ManagedThread>{
ArrayList<ManagedThread> list = new ArrayList<>();
@Override
public ManagedThread getResource() {
ManagedThread t = null;
if (list.size() == 0) {
t = new ManagedThread(this);
System.out.printf("새 스레드 생성: %d\n", t.no);
t.start();
try {
Thread.sleep(100);
} catch (Exception e) {}
return t;
}
t = list.remove(0);
System.out.printf("기존 스레드 리턴: %d\n", t.no);
return t;
}
@Override
public void returnResource(ManagedThread resource) {
list.add(resource);
System.out.printf("스레드 반납: %d\n", resource.no);
}
}
Thread Pool을 활용한 서버 애플리케이션 구현
- ServerApp : 클라이언트의 요청을 받아 해당 요청을 처리하는 스레드를 생성하거나 재사용
ServerApp
ServerApp은 소켓을 통해 클라이언트의 요청을 받아들입니다. 그런 다음, 각 요청에 대해 새로운 ManagedThread를 만들거나 기존의 하나를 재사용하여 요청을 처리합니다. 이 과정에서 이전에 만들었던 ThreadPool이 활용됩니다.
public class ServerApp {
public static void main(String[] args) {
// Set up the thread pool
ThreadPool threadPool = new ThreadPool();
try (ServerSocket serverSocket = new ServerSocket(8888)) {
while (true) {
Socket socket = serverSocket.accept();
// Get a thread from the pool
ManagedThread t = threadPool.getResource();
// Set the job for the thread
t.setJob(() -> processRequest(socket));
}
} catch (IOException e) {
System.err.println("서버 준비 중 오류 발생!");
e.printStackTrace();
}
}
public static void processRequest(Socket socket) {
// Insert request processing code here...
}
}
processRequest()
processRequest() 메소드는 클라이언트의 요청을 처리하는 역할을 합니다. 이 메소드 내에서 클라이언트의 요청을 받아들이고, 적절한 응답을 전달하는 로직을 작성합니다.
public void processRequest(Socket socket) {
try (Socket s = socket;
DataInputStream in = new DataInputStream(socket.getInputStream());
DataOutputStream out = new DataOutputStream(socket.getOutputStream())) {
// Processing request logic goes here...
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
'Programming > Java' 카테고리의 다른 글
[ JAVA ] 스레드 풀(Thread Pool) - Thread Pool을 사용해야 하는 이유 (0) | 2023.07.16 |
---|---|
[ JAVA ] 클라이언트 요청 순차적 처리 - Stateful VS Stateless (0) | 2023.07.12 |
[ JAVA ] JAVA 예외 처리 (Exception) - Try-Catch-Finally 문법, 예외 메시지와 스택 추적 (0) | 2023.07.10 |
[ JAVA ] JAVA 익명 클래스(Anonymous Class) (0) | 2023.07.08 |
[ JAVA ] FileInputStream 확장하여 DataInputStream 클래스 구현하기 ( java.io.DataInputStream ) (0) | 2023.06.29 |