[Servlet] 자바 서블릿과 네트워크 | 요청과 응답이 오가는 과정을 파헤쳐보자
[Servlet] 자바 서블릿과 네트워크 | 요청과 응답이 오가는 과정을 파헤쳐보자
[Servlet] Servlet과 JSP로 구현하는 MVC 패턴 | Java 웹 구조의 이해 - 2 [Servlet] Servlet과 JSP로 구현하는 MVC 패턴 | Java 웹 구조의 이해 - 2[Servlet] Servlet과 JSP로 구현하는 MVC 패턴 | Java 웹 구조의 이해 - 1 [Serv
crushed-taro.tistory.com
1. Thread
1. Thread 개요
1. Process와 Thread
- Process
- 실행 중인 프로그램을 가리키며, 프로그램이 실행될 때마다 개별적으로 생성된다.
- 하나의 프로세스는 프로그램을 수행함에 있어 필요한 데이터, 메모리 등의 할당 받은 자원, 하나 이상의 스레드로 구성된다.
- Thread
- 프로세스 내에서 할당된 자원을 이용해 실제 작업을 수행하는 작업 단위로, 모든 프로세스는 하나 이상의 스레드를 가지며 각각 독립적인 작업 단위를 지닌다.
2. main thread
- 모든 자바 프로그램은 메인 스레드가 main() 메소드를 실행하며 시작되어, main() 메소드의 첫 코드부터 아래로 순차적으로 실행되다가 return을 만나면 종료된다.
- 필요에 의해 작업 스레드들을 만들어서 병렬 코드를 실행(= 멀티 스레드를 이용한 멀티 태스킹)할 수 있다.
- 싱글 스레드는 메인 스레드가 종료되면 프로세스도 종료되지만, 멀티 스레드는 실행 중인 스레드가 하나라도 있다면 프로세스가 종료되지 않는다.
2. Multi Thread
1. Multi Process와 Multi Thread
- 멀티 프로세스 : 각각의 프로세스를 독립적으로 실행한다.
- 멀티 스레드 : 하나의 프로세스 내에서 여러 스레드가 동시에 작업을 수행한다.
2. Single Thread와 Multi Thread
- 싱글 스레드 : 메인 스레드 하나만 가지고 작업을 처리하며, 한 작업씩 차례대로 처리한다.
- 멀티 스레드 : 메인 스레드 외의 추가적인 스레드를 이용하여 병렬적으로 작업을 처리한다.
- 멀티 스레드의 장점
- 자원을 보다 효율적으로 사용할 수 있다.
- 사용자에 대한 응답성이 향상된다.
- 애플리케이션의 응답성이 향상된다.
- 작업이 분리되어 코드를 간결하게 작성할 수 있다.
- CPU 효율성이 향상된다.
- 멀티 스레드의 단점
- 동기화(= Synchronization)에 주의해야 한다.
- 교착상태(= dead-lock)가 발생하지 않도록 주의해야 한다.
- 이처럼 프로그래밍 시 고려해야 할 사항이 많다.
3. Thread 사용
1. Thread 구현 방법
- Thread 클래스를 상속 받아 구현하는 방법이 있다.
- Runnable 인터페이스로 구현하는 방법이 있다.
2. Thread 동작 구조
- run() 호출
- start() 호출
- 스레드 스케줄링
3. Java Thread Scheduling 방식
- 우선 순위 방식 (Priority)
- 우선 순위가 높은 스레드가 작업 시간을 더 많이 가지도록 하는 스케쥴링 방식이다.
- 스레드에 1부터 10까지 우선 순위 번호를 부여할 수 있으며, 번호가 높을수록 우선 순위가 높다.
- 스레드 생성 시 우선 순위 기본값은 5이다.
- 사용 예시
public class Run {
public static void main(String[] args) {
클래스명 레퍼런스 = new 생성자(); //Thread를 상속한 객체 생성
레퍼런스.setPriority(1 ~ 10);
}
}
- 순환 할당 방식 (Round-Robin)
- 시간 할당량(Time Slice)을 정하여 하나의 스레드를 정해진 시간만큼 실행시키는 방식이다.
- JVM에 의해 정해지기 때문에 코드로 제어가 불가하다.
4. Thread control
- 효율적이고 정교한 스케쥴링을 위하여 실행 중인 thread의 상태를 제어하는 기능을 한다.
- Thread control의 주요 method는 다음과 같다.
method명 | 설명 |
void interrupt() | - (sleep() 이나 join() 에 의해) 일시정지 상태인 스레드를 실행대기 상태로 변경 - 해당 스레드에서 InterruptException이 발생해 일시정지 벗어남 |
void join() void join(long millis) void join(ling millis, int nanos) |
- 지정된 시간 동안 스레드 실행시킴 - 지정 시간이 지나거나 작업이 종료되면 join() 호출한 스레드로 돌아와 실행 계속함 |
static void sleep(long millis) static void sleep(long millis, int nanos) |
- 지정된 시간 동안 스레드 일시정지 시킴 - 지정 시간이 지나면 자동으로 실행대기 상태로 변경 |
static void yield() | 실행 중 다른 스레드에게 양보하고 실행대기 |
void wait() void wait(long timeout) void wait(long timeout, int nanos) |
동기화 된 블록 안에서 다른 스레드가 이 객체의 notify(), notifyAll()을 호출하거나 지정된 시간이 지날 때까지 현재 thread 대기시킴 |
void notify() | - 동기화 된 블록 안에서 호출한 객체 내부에서 대기 중인 스레드 깨움 - 여러 스레드가 있을 경우 임의의 스레드 하나에만 통보 |
void notifyAll() | - 동기화 된 블록 안에서 호출한 객체 내부에서 대기 중인 모든 스레드 깨움 - 하지만 lock은 하나의 스레드만 얻을 수 있음 |
4. 동기화 (Synchronized)
1. 동기화란
- 한번에 한 개의 스레드만 프로세스 공유 자원(= instance)에 접근할 수 있도록 락(Lock)을 걸어 다른 스레드가 진행 중인 작업에 간섭하지 못하도록 하는 것이다.
2. 동기화 method와 동기화 block
- 동기화 method
public synchronized void method() {
// 한 개의 스레드만 실행할 수 있다.
}
- 동기화 블록
public void method1() {
// 여러 스레드를 실행할 수 있다.
synchronized (공유 인스턴스) {
// 한 개의 스레드만 실행할 수 있다.
}
// 여러 스레드를 실행할 수 있다.
}
5. Daemon Thread
1. Daemon Thread란
- 다른 스레드의 작업을 돕는 보조 역할을 수행하는 스레드이다.
- 데몬 스레드 이외의 스레드가 모두 종료되면 데몬 스레드는 강제로 종료된다.
- ex) 가비지 컬렉션, 워드 프로세서의 자동 저장, 화면 자동 갱신 등
2. Daemon Thread 생성
- 데몬 스레드가 될 스레드의 레퍼런스 변수에 setDaemon(true)를 호출하여 생성한다.
- 단, start() method 호출 전에 setDaemon(true)를 호출해야 한다.
- → 그러지 않으면 IllegalThreadStateException이 발생한다.
6. 실습 예제
1. 간단한 웹 서버 구현 예제
- 요청 시 Header 값을 출력하는 간단한 웹서버를 구현하는 코드이다.
- 클라이언트가 요청을 한번 보내면 서버에는 총 3번의 요청이 들어온다. 왜냐하면 브라우저가 favicon 요청을 위해 request를 다시 한 번 보내기 때문이다.
- 해당 예제에서는 favicon 설정 없이 진행하므로 3번째 요청에 대해서만 처리하도록 구현한다.
public static void main(String[] args) throws IOException {
/* 클라이언트의 요청을 기다릴 ServerSocket 생성 */
ServerSocket listener = new ServerSocket(8002);
/* 요청 횟수를 판단하기 위한 변수 선언 */
int count = 1;
try {
System.out.println("Http Server started at 8002 port");
while (true) {
/* 클라이언트 요청이 들어오면 통신할 socket 생성 */
Socket socket = listener.accept();
try {
/* 요청한 클라이언트의 정보 받아옴 */
System.out.printf("New Client Connect! Connected IP : %s, Port : %d\n",
socket.getInetAddress(), socket.getPort());
count++;
InputStream in = socket.getInputStream();
OutputStream out = socket.getOutputStream();
/* 파비콘 요청을 위한 요청은 건너뛰고 header 내용은 3번째 요청인 본 요청으로 읽음 */
int value = 0;
while((value = in.read()) != -1 && count == 3) {
System.out.print((char)value);
}
String responseText = "<h1>Hello World</h1>";
String responseGeneralHeader = "HTTP/1.1 200 OK \r\n";
String contentType = "Content-Type: text/html; charset=utf-8\r\n";
String contentLength = "Content-Length: " + responseText.length() + "\r\n";
String whiteLine = "\r\n";
/* 패킷 통신을 위해 byte 단위로 출력하도록 함 */
out.write(responseGeneralHeader.getBytes());
out.write(contentType.getBytes());
out.write(contentLength.getBytes());
out.write(whiteLine.getBytes());
out.write(responseText.getBytes());
out.write(whiteLine.getBytes());
out.flush();
} finally {
/* 자원 반납 */
socket.close();
}
}
} finally {
/* 자원 반납 */
listener.close();
}
}
2. Multi Thread를 생성하는 간단한 웹 서버 예제
- 요청 시마다 멀티 스레드를 생성하여 Header 값을 출력하는 간단한 웹 서버를 구현하는 코드이다.
public static void main(String[] args) throws IOException {
// 8000번 포트로 서버소켓 생성
ServerSocket serverSocket = new ServerSocket(8000);
Socket client;
while ((client = serverSocket.accept()) != null) {
Socket finalClient = client;
/* client socket의 요청을 승인하여 연결될 때마다 스레드 생성 */
new Thread(() -> {
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(finalClient.getInputStream()));
// General header parsing
String generalHeader = reader.readLine();
System.out.println("generalHeader = " + generalHeader);
String requestMethod = generalHeader.split(" ")[0];
String requestPath = generalHeader.split(" ")[1];
System.out.println("requestMethod = " + requestMethod);
System.out.println("requestPath = " + requestPath);
// Request header parsing
Map<String, String> requestHeader = new HashMap<>();
String line;
while ((line = reader.readLine()) != null) {
/* 읽어온 라인이 비어있지 않은 경우 */
if (line.isBlank()) {
break;
}
/* 읽어온 라인을 :(콜론)을 기준으로 나누어 key-value 구분해서 받음 */
String key = line.split(": ")[0];
String value = line.split(": ")[1];
requestHeader.put(key, value);
}
System.out.println("requestHeader = " + requestHeader);
// POST방식의 요청인 경우 body parsing
String body = null;
if ("POST".equals(requestMethod)) {
int contentLength = Integer.parseInt(requestHeader.get("Content-Length"));
char[] temp = new char[contentLength];
reader.read(temp);
body = new String(temp).trim();
}
System.out.println("body = " + body);
} catch (IOException e) {
throw new RuntimeException(e);
}
}).start();
}
}
'이것 저것 개발 공부 > Servlet' 카테고리의 다른 글
[Servlet] 자바 서블릿과 네트워크 | 요청과 응답이 오가는 과정을 파헤쳐보자 (0) | 2025.05.25 |
---|---|
[Servlet] Servlet과 JSP로 구현하는 MVC 패턴 | Java 웹 구조의 이해 - 2 (0) | 2025.05.24 |
[Servlet] Servlet과 JSP로 구현하는 MVC 패턴 | Java 웹 구조의 이해 - 1 (0) | 2025.05.22 |
[Servlet] Servlet Listener란? 웹 애플리케이션의 생명주기 관리하기 (0) | 2025.05.20 |
[Servlet] Servlet Filter 구현 예제와 실전 적용 방법 (0) | 2025.05.15 |