https://en.wikipedia.org/wiki/Thread_(computing) A process with two threads of execution, running on one processor

프로세스

운영체제에 의해 메모리 공간을 할당받아 CPU에서 실행/제어되고 있는 프로그램이다.

종종 '스케줄링의 대상이 되는 작업'이라는 용어와 거의 같이 쓰인다.

데이터(data) + 자원(memory) + 스레드(Thread)로 구성돼 실제로 프로그램이 실행되기까지는

스레드가 데이터+자원을 활용하여 작업을 수행한다.

 

💡 프로그램이란 파일 시스템에 존재하는 실행파일이다.


스레드

하나의 프로세스 내에서 여러 개의 실행 흐름(단일, 동시적, 병렬적)을 두어 작업을 효율적으로 처리하기 위한 모델이다.

프로그램 실행하면 JVM이 시작되고 JVM이 시작되면 자바 프로세스가 시작된다. 이때 프로세스 안에 스레드가 있다.

여러 프로세스가 공유하는 하나의 스레드가 수행되는 일은 없다.

어떤 프로세스든 하나 이상의 스레드가 수행된다.

두 개 이상의 스레드를 가지는 프로세스멀티스레드 프로세스라고 한다.

멀티스레드주어진 자원을 극한으로 사용하기 위한 모델이다.

 


스레드 사용 이유

메모리 절약

OS마다 다르지만, 무슨 작업을 수행하려고 할 때 JVM은 적어도 32~64MB 물리 메모리 점유한다.

근데 스레드는 1MB 이내의 메모리만 점유한다. 그래서 스레드를 '경량 프로세스'라고도 부른다.

 

프로세스 콘텍스트 스위칭(Context Switching)에 비해 오버헤드 절감

멀티 프로세스로 실행되는 작업을 멀티 스레드로 실행하게 되면 프로세스를 생성하여 자원을 할당하는 과정도 줄어들뿐더러 프로세스를 콘텍스트 스위칭(Context Switching)하는 것보다 오버헤드를 더 줄일 수 있게 된다.

 

💡 콘텍스트 스위칭(Context Switching)에 대해 궁금하신 분들은 별도로 포스팅했으니 참고 바란다. 

 

작업들 간 통신 비용 절감

프로세스 간의 통신 비용보다 하나의 프로세스 내에서 여러 스레드 간의 통신 비용이 훨씬 적으므로 작업들 간의 통신 부담을 줄일 수 있게 된다.

 

예) Google Docs

 

하나의 Google Docs를 프로세스라 생각하고 문서에 참여하는 사용자들을 각각 스레드라 생각해보자.

만약 멀티 프로세스라면 사용자 한 명당 하나의 Google Docs를 켜서 자신이 해야 하는 임무를 마무리하고 도출된 결과를 합쳐야 할 것이고, 멀티 스레드라면 하나의 Google Docs에서 여러 사람들이 분배받은 커서를 이용하여 자신의 임무를 마무리하면 된다.

사용자 수에 따른 여러 개의 Google Docs가 아닌 하나의 Google Docs를 만들었기에 Google Docs 개수 차이만큼 자원 할당을 줄였고, 여러 개의 Google Docs를 하나의 Google Docs로 합치는 것을 통신 비용이라 생각한다면 하나의 Google Docs에서 여러 사용자들이 함께 임무를 수행하는 게 통신 비용을 줄일 수 있어 더 효율적이라고 생각할 수 있다.

 


스레드와 프로세스의 차이점

프로세스는 완벽히 독립적이기 때문에 메모리 영역을 다른 프로세스와 공유를 하지 않지만

스레드는 해당 스레드를 위한 스택을 생성할 뿐 프로세스 내의 메모리를 공유해서 사용할 수 있다.

https://mooneegee.blogspot.com/2015/01/os-thread.html

 


스레드 상태

아래의 각 스레드 상태들이 JVM에 의해 생성/관리된다.

https://velog.io/@ruthetum/JAVA-Thread-Multitasking

 

NEW

스레드 객체는 생성됐지만 아직 스레드 대기열 큐에 올라가지 않았고 start() 되지 않은 상태이다.

RUNNABLE

start()가 호출돼 실행 대기 중인 상태이다. run() 이 호출되면 running 상태가 된다.

WAITING

일시정지 상태이며 다른 스레드의 통지(nofity)를 기다리는 상태이다.

TIMED_WAITING

일시정지 상태이며 일정 시간 동안 기다리는 상태이다.

TERMINATED

스레드 실행을 마치고 종료한다. run() 이 끝나면 소멸된다.

BLOCK

일시정지 상태이며 사용하려는 객체의 모니터 락(monitor lock)이 풀리기를 기다리는 상태이다.

 

참고로 자바, 스프링에서는 스레드를 내부적으로 관리해주면서 스레드 종료까지 시켜주진 않는다.

이는 개발자가 직접 처리해야 하는 것이지 시스템에 맡기는 것이 아니다.

 


스레드 우선순위

스레드 우선순위란 대기하고 있는 상황에 더 먼저 수행할 수 있는 순위를 말한다.

우선순위 값은 스레드 간 관계를 잘 파악하고 있는 상태에서 변경해야 장애가 발생하지 않는다.

따라서 기본값을 사용하는 것을 권장한다.

 

💡 MAX_PRIORITY : 10 / NORM_PRIORITY : 5 (기본값) / MIN_PRIORITY : 1

 

스레드 여러 개가 순서대로 실행돼도 각 스레드가 끝나는 순서는 정해져 있지 않다.

또한 새로 생성한 스레드는 run() 메서드가 종료되면 끝난다.

run() 메서드가 안 끝나면 애플리케이션도 안 끝난다.

 


스레드 이름

모든 스레드에는 이름이 있다. 아무 이름도 지정 안 하면 "Thread-n" 형식으로 지정된다.

여기서 n은 스레드가 생성된 순서에 따라 증가한다. 이름이 겹친다고 해도 예외나 에러가 발생하진 않는다.

스레드 이름을 생성하는 이유 - 스레드 디버깅 용이

멀티스레드 프로그램 환경에서 버그나 치명적인 오류가 발생했을 때 디버깅으로 원인 파악을 하는데,

이때 이름을 통해 어떤 스레드가 무슨 작업을 하고 있는지 파악하기 용이하기 때문이다.

 


스레드 처리 스택 사이즈

스레드에서 말하는 스택이란 Collection의 Stack이 아니고

JVM 메모리 영역(JVM Runtime data area)의 스택 영역(Stack Area)을 말한다.

 

💡 JVM 메모리 영역과 스택 영역이 궁금하다면 메모리 영역(Runtime Data Area, JVM Memory) 포스팅을 참고 바란다.

 

얼마나 많은 스레드가 동시에 처리되는지는 JVM이 실행되는 OS 플랫폼에 따라 다르다.

Thread(ThreadGroup group, Runnable target, String name, long stackSize)

그래서 위 생성자에서 stackSize는 무시될 수도 있다.


Main 스레드

main 메서드도 하나의 스레드를 갖는데 이를 메인 스레드(Main Thread)라고 한다.

메인 스레드는 프로그램이 시작하면 가장 먼저 실행되는 스레드이며 모든 스레드는 메인 스레드로부터 생성된다.

 

다른 스레드를 생성해서 실행하지 않는다면 메인 스레드가 종료되는 순간 프로그램도 종료된다.

하지만, 여러 스레드를 실행하면 메인 스레드가 종료돼도

다른 스레드가 작업을 마칠 때까지 프로그램이 종료되지 않는다.

 

스레드는 '사용자 스레드(User Thread)'와 '데몬 스레드(Daemon Thread)'로 구분되는데,

실행 중인 사용자 스레드가 하나도 없을 때 프로그램이 종료된다.

 

https://www.geeksforgeeks.org/main-thread-java/?ref=lbp


데몬 스레드

다른 일반 스레드의 작업을 돕는 보조적인 역할을 수행하는 스레드이다.

단, 해당 스레드가 시작( start() )되기 전 setDeamon(true)로 데몬 스레드 지정을 해야 한다.

 

자기 자신이 종료되지 않아도 다른 실행 중인 일반 스레드가 없다면 멈춰 버린다.

예를 들어 main 스레드에 데몬 스레드를 생성해두고 main 스레드를 종료하면 데몬 스레드도 같이 종료된다.

 

데몬 스레드 사용 이유

예를 들어 모니터링하는 스레드를 별도로 띄워 모니터링하다가,

주요 스레드가 종료되면 관련된 모니터링 스레드가 종료돼야 프로세스가 종료될 수 있다.

그런데 모니터링 스레드를 데몬 스레드로 만들지 않으면 프로세스가 종료될 수 없게 된다.

 

이렇게, 부가적인 작업을 수행하는 스레드를 선언할 때 데몬 스레드를 만든다.

대표적인 데몬 스레드로는 가비지 컬렉션(Garbage Collection)이 있다.

 

💡 가비지 컬렉션이 궁금하다면 가비지 컬렉터(Garbage Collector) - 가비지 개념과 가비지 컬렉션 프로세스 포스팅을 참고 바란다.


스레드 그룹

어떤 스레드를 생성할 때 스레드를 집합 단위로 관리할 수 있다. 그게 바로 스레드 그룹(ThreadGroup)이다.

이렇게 하면 ThreadGroup 클래스에서 제공하는 여러 메서드를 통해 각종 정보를 얻을 수 있다.

용도가 다른 여러 스레드를 관리할 수 있는 클래스다.

ThreadGroup은 기본적으로 트리구조를 가진다.

https://skyjumps.tistory.com/?page=69

 

모든 스레드는 반드시 하나의 스레드 그룹에 속하며 스레드 생성 시 스레드 그룹을 지정해주지 않으면 기본값으로 main 스레드 그룹에 속하게 된다. 스레드는 자신을 생성한 부모 스레드의 그룹과 우선순위를 상속받기 때문이다.


출처

멀티 프로세스, 멀티 스레드 비교 예시

https://www.crocus.co.kr/1510

 

스레드 상태

https://velog.io/@ruthetum/JAVA-Thread-Multitasking

 

스레드 우선순위

docs.oracle.com/javase/7/docs/api/java/lang/Thread.html#NORM_PRIORITY

 

Main 스레드

https://wisdom-and-record.tistory.com/48