스레드 학습을 진행하고 있습니다. 이전 게시물 이후에 추가 학습한 내용입니다.
스레드란 무엇인가: Spring Boot 모니터링을 하다 생긴 궁금증 (2)
https://doitwojae.tistory.com/entry/%EC%8A%A4%EB%A0%88%EB%93%9C%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80-Spring-Boot-%EB%AA%A8%EB%8B%88%ED%84%B0%EB%A7%81%EC%9D%84-%ED%95%98%EB%8B%A4-%EC%83%9D%EA%B8%B4-%EA%B6%81%EA%B8%88%EC%A6%9D-1 스레드란 무엇
doitwojae.tistory.com
이번 글은 인프런 김연한 실전 자바 - 고급 1편, 멀티 스레드와 동시성을 통해서 학습한 내용을 정리합니다.
김영한의 실전 자바 - 고급 1편, 멀티스레드와 동시성 강의 | 김영한 - 인프런
김영한 | , 국내 개발 분야 누적 수강생 1위, 제대로 만든 김영한의 실전 자바[사진][임베딩 영상]단순히 자바 문법을 안다? 이걸로는 안됩니다!전 우아한형제들 기술이사, 누적 수강생 40만 명 돌
www.inflearn.com
스레드 생성과 실행
스레드를 제대로 이해하려면 자바 메모리 구조를 확실하게 이해하고 있어야 한다.
자바 메모리 구조
- 메서드 영역 (Method Area) : 메서드 영역은 프로그램을 실행하는데 필요한 공통 데이터를 관리한다. 이 영역은 프로그램의 모든 영역에서 공유한다.
- 클래스 정보 : 클래스 실행 코드 (바이트 코드), 필드, 메서드와 생성자 코드 등 모든 실행 코드가 존재한다.
- static 영역 : 프로그램을 실행하는데 필요한 공통 리터럴 상수를 보관한다.
- 런타임 상수 : 프로그램을 실행하는데 필요한 공통 리터럴 상수를 보관한다.
- 스택 영역 (Stack Area) : 자바 실행 시 하나의 실행 스택이 생성된다. (main 스택) 각 스택 프레임은 지역 변수, 중간 연산 결과, 메서드 호출 정보 등을 포함한다.
- 스택 프레임 : 스택 영역에 쌓이는 네모 박스 하나의 스택 프레임이다. 메서드를 호출할 때마다 하나의 스택 프레임이 쌓이고, 메서드가 종료되면 스택 프레임이 제거된다.
- 힙 영역 (Heap Area) : 객체 (인스턴스)와 배열이 생성되는 영역이다. 가비지 컬렉션 (GC)이 이루어지는 주요 영역이며, 더 이상 참조되지 않는 객체는 GC에 의해 제거된다.
참고
스택 영역은 더 정확히는 각 스레드별로 하나의 실행 스택이 생성된다. 따라서 스레드 수만큼 스택이 생성된다. 지금은 스레드를 1개만 사용하므로 스택도 하나다. 이후 스레드를 추가할 것인데, 그러면 스택도 스레드 수만큼 증가한다
실행 결과를 보면 main( ) 메서드는 main이라는 이름의 스레드가 실행되는 것을 확인할 수 있다. 프로세스가 작동하려면 스레드가 최소한 하나는 있어야 한다. 그래야 코드를 실행할 수 있다. 자바는 실행 시점에 main 이라는 이름의 스레드를 만들고 프로그램의 시작점인 main( ) 메서드를 실행한다.
main 스레드가 새로 생성한 스레드의 start 메서드를 통해서 스레드에게 실행을 지시한다. main 스레드가 run을 직접 호출하는 것이 아니다. main 스레드는 다른 스레드에게 일을 시작하라고 지시만 하고 바로 start 메서드를 빠져나온다.
데몬 스레드
스레드는 두 가지 종류가 존재한다.
- 사용자 스레드
- 데몬 스레드
🐻 사용자 스레드 (non-daemon)
- 프로개름의 주요 작업을 수행한다.
- 작업이 완료될 때까지 실행한다.
- 모든 user 스레드가 종료되면 JVM도 종료된다.
🐻 데몬 스레드 (daemon)
- 백그라운드에서 보조적인 작업을 수행한다 (모니터링)
- 모든 user 스레드가 종료되면 데몬 스레드는 자동으로 종료된다.
JVM은 데몬 스레드의 실행 완료를 기다리지 않고 종료된다. 데몬 스레드가 아닌 모든 스레드가 종료되면, 자바 프로그램도 종료된다.
용어 : 데몬
그리스 신화에서 데몬은 신과 인간 사이의 중간적 존재로, 보이지 않게 활동하며 일상적인 일들을 도왔다. 이런 의미로 컴퓨터 과학에서는 사용자에게 직접적으로 보이지 않으면서 시스템의 백그라운드에서 작업을 수행하는 것을 데몬 스레드, 데몬 프로세스라 한다.
💡 참고
run( ) 메서드 안에서 예외를 throws 할 수 없다. 예외를 잡아서 처리해주어야 한다. 메서드 안에서 Thread.sleep()을 호출할 때 체크 예외인 InterruptedException을 밖으로 던질 수 없고 반드시 잡아야 한다.
- run 메서드는 체크 예외를 밖으로 던질 수 없다.
스레드 생성 - Runnable
Functional Interface 인 Runnable 를 구현하도록 한다면, 스레드 클래스와 해당 스레드가 실행할 작업이 서로 분리된다. 스레드 객체를 생성할 때, 실행할 작업을 생성자로 전달하면 된다.
스레드를 사용할 때는 Thread 클래스를 extends 하는 것보다 Runnable 인터페이스를 구현하는 방식을 사용하자.
두 방식이 서로 장단점이 있지만, 스레드를 생성할 때는 상속하는 방식보다 인터페이스를 구현하는 방식이 더 나은 선택이다. 왜냐하면 자바는 여러 클래스를 다중 상속하는 것이 불가능하기 때문이다.
- 상속의 자유로움
- 코드의 분리 : 스레드와 실행할 작업을 분리하여 코드의 가독성을 높일 수 있다.
- 여러 스레드가 동일한 Runnable 객체를 공유할 수 있어 자원 관리를 효율적으로 할 수 있다.
정리하자면 Runnable 인터페이스를 구현하는 방식을 사용하자. 스레드와 실행할 작업을 명확히 분리하고, 인터페이스를 사용하므로 Thread 클래스를 상속하는 방식보다 더 유연하고 유지보수 하기 쉬운 코드를 만들 수 있다.
🐻 로거 만들기
어떤 스레드가 코드를 실행하는지 출력하기 위해 다음과 같이 현재 시간, 스레드 이름, 출력 내용등이 나오는 편리한 기능을 만든다.
public abstract class MyLogger {
private static final DateTimeFormateer fomatter =
DateTimeFormatter.ofPattern("HH:mm:ss.SSS");
public static void log(Object obj) {
String time = LocalTime.now().format(formatter);
System.out.printf("%s [%9s] %s\n", time, Thread.currentThread().getName(), obj);
}
public static void main(String[] args) {
log("Hello, World!");
log(123);
}
}
실행 결과
16:06:54.536 [ main] Hello, World!
16:06:54.538 [ main] 123
스레드를 학습할 때는 스레드 이름, 그리고 해당 스레드가 언제 실행되었는지 확인하는 것이 중요하다.
참고
김영한의 실전 자바 - 고급 1편 스레드
김영한의 실전 자바 - 고급 1편, 멀티스레드와 동시성 강의 | 김영한 - 인프런
김영한 | , 국내 개발 분야 누적 수강생 1위, 제대로 만든 김영한의 실전 자바[사진][임베딩 영상]단순히 자바 문법을 안다? 이걸로는 안됩니다!전 우아한형제들 기술이사, 누적 수강생 40만 명 돌
www.inflearn.com