[CS/운영체제] 멀티스레드와 동시성 - 3부

황제연·2025년 6월 26일
0

CS학습

목록 보기
117/193
post-thumbnail

JAVA에서 제공하는 스레드 기본정보

스레드 생성

스레드를 생성할 때는 실행하려는 Runnable 인터페이스의 구현체와
스레드 이름을 전달할 수 있습니다

Thread thread = new Thread(new MyRunnable(), "thread");
  • Runnable 인터페이스: 실행할 작업을 포함하는 인터페이스입니다
  • 스레드 이름: "thread"라는 이름으로 스레드를 생성합니다
    이름을 생략하면 임의의 이름이 생성됩니다

스레드 객체 정보

System.out.println("thread = ", thread);

스레드 객체를 문자열로 변환해서 바로 출력합니다

스레드 아이디

System.out.println("thread = ", thread.threadId());

threadId()

스레드의 고유 식별자를 반환하는 메소드입니다
해당 ID는 JVM 내에서 각 스레드에 대해 유일합니다

ID는 스레드가 생성될 때 할당되며, 직접 지정할 수 없습니다

스레드 이름

System.out.println("thread = ", thread.getName());

스레드의 이름을 반환합니다. 스레드 이름은 중복될 수 있습니다

스레드 우선순위

System.out.println("thread = ", thread.getPriority());

스레드의 우선순위를 반환하는 메소드입니다
우선순위는 1부터 10까지 설정할 수 있고 1이 가장 낮으며 5가 기본값입니다

setPriority()를 통해 우선순위를 변경할 수 있습니다

우선순위는 스레드 스케줄러가 어떤 스레드를 우선 실행할지 결정하는데 사용됩니다
하지만 실제 실행 순서는 JVM 구현과 운영체제에 따라 달라질 수 있습니다

스레드 그룹

System.out.println("thread = ", thread.getThreadGroup());

스레드가 속한 그룹을 반환하는 메소드입니다
스레드 그룹은 스레드를 그룹화해서 관리할 수 있으며,
기본적으로 모든 스레드는 부모 스레드와 동일한 스레드 그룹에 속하게 됩니다

스레드 그룹은 하나의 그룹으로 묶어서 일괄 종료 등의 특정 작업을 수행할 수 있습니다

부모 스레드

새로운 스레드를 생성하는 스레드를 부모 스레드라고 합니다
스레드는 기본적으로 다른 스레드에 의해 생성되며, 생성된 스레드를 자식, 생성한 주체를 부모로 판단합니다

main 스레드는 기본적으로 제공되는 main 스레드 그룹에 소속되어 있습니다

스레드 상태

System.out.println("thread = ", thread.getState());

스레드의 현재 상태를 반환하는 메소드입니다
주요 상태는 다음과 같습니다

  • NEW: 스레드가 아직 시작되지 않은 상태
  • RUNNABLE: 스레드가 실행중이거나 실행될 준비가 된 상태입니다
  • BLOCKED: 스레드가 동기화 락을 기다리는 상태입니다
  • WAITING: 스레드가 다른 스레드의 특정 작업이 완료되기까지 기다리는 상태입니다
  • TIMED_WAITING: 일정 시간 동안 기다리는 상태입니다
  • TERMINATED: 스레드가 실행을 마친 상태입니다

스레드의 생명 주기

스레드의 상태

  • NEW: 스레드가 생성되었고 아직 시작되지 않은 상태
  • RUNNABLE: 스레드가 실행중이거나 실행될 준비가 완료된 상태
  • BLOCKED: 스레드가 동기화 락을 기다리는 상태
  • WAITING: 스레드가 무기한으로 다른 스레드 작업을 기다리는 상태
  • TIMED WAITING: 스레드가 일정시간동안 다른 스레드의 작업을 기다리는 상태
  • TERMINATED: 스레드의 실행이 완료된 상태

NEW

스레드가 생성되고 아직 시작되지 않은 상태로 Thread 객체는 생성되었지만
start() 메소드가 호출되지 않은 상태입니다

RUNNABLE

스레드가 실행 준비 완료된 상태입니다
이 상태에서 스레드는 CPU에서 실행될 수 있습니다

start() 메소드가 호출되면 스레드는 RUNNABLE 상태가 됩니다
다만 모든 RUNNABLE 상태에 있는 스레드가 동시에 실행되지는 않습니다
운영체제의 스케줄러가 각 스레드에 CPU시간을 할당하여 실행하기 때문에
스레드는 스케줄러의 실행 대기열에 포함되어 있다가 차례대로 CPU에서 실행됩니다

자바에서는 운영체제 스케줄러의 실행대기열이든지 CPU에서 실행되든지
둘다 RUNNABLE 상태이기 때문에 구분해서 확인할 수 없으며 보통 실행상태라고 부릅니다

BLOCKED

스레드가 다른 스레드에 의해 동기화 락을 얻기 기다리는 상태로
synchronized 블록에 진입하기 위해 락을 얻어야 하는 경우를 말합니다

WAITING

스레드가 다른 스레드의 특정 작업이 완료되기를 무기한 기다리는 상태입니다
wait(), join() 메소드가 호출될 때 이 상태가 됩니다

스레드는 다른 스레드가 notify() 또는 notifyAll() 메소드를 호출하거나
join()이 완료될때까지 기다립니다

TIMED WAITING

스레드가 특정 시간동안 다른 스레드의 작업이 완료되기까지 기다리는 상태입니다
sleep(long millis), wait(longo timeout), join(long millis) 메소드가 호출될 때 이 상태가 됩니다

주어진 시간이 경과하거나 다른 스레드가 해당 스레드를 깨우면 이 상태에서 벗어납니다

TERMINATED

스레드의 실행이 완료된 상태입니다
스레드가 정상적으로 종료되거나 예외가 발생해서 종료된 경우 이 상태가 됩니다
스레드는 한번 종료하면 다시 시작할 수 없습니다

자바 스레드의 상태 전이 과정

  1. NEW -> RUNNABLE
  2. RUNNABLE -> BLOCKED/WAITING/TIMED WAITING
  3. BLOCKED/WAITING/TIMED WAITING -> RUNNABLE
  4. RUNNABLE -> TERMAITED

Runnable과 체크예외

public interface Runnable {
	void run();
}

위와같은 Runnable 인터페이스의 run() 메소드를 구현하면 InterruptedException 체크 예외를
밖으로 던질 수 없습니다

자바에서 메소드를 재정의 할 때 반드시 부모 메소드가 체크 예외를 던지지 않으면
재정의된 자식 메소드도 체크 예외를 던질 수 없기 때문입니다

자식 메소드는 부모 메소드가 던질 수 있는 체크 예외의 하위 타입만 던질 수 있습니다

이러한 제약을 두는 이유는?

자식 클래스가 더 넓은 범위의 예외를 던지면 모든 예외를 제대로 처리하지 못할 수 있고
일관성을 망치며, 예상치 못한 런타임 오류도 발생할 수 있습니다

class Parent{
	void method() throws InterruptedException{
	}
}

class Child extends Parent{
	@Override
	void method() throws Exception{
	}
}

public class Main{
	public static void main(String[] args){
		Parent p = new Child();
		try{
			p.method();
		} catch (InterruptedException e){
		}
	}
}

이런 코드가 있으면 자바 컴파일러는 Parent p의 method를 호출한 것으로 인지합니다

이런경우 child에서 전혀 다른 예외를 반환하면 클라이언트는 해당 예외를 잡을 수 없습니다

따라서 체크 예외는 재정의할 때, 부모 메소드가 던질 수 없거나 원래 체크 예외를 던지지 않으면
체크예외를 던지도록 작성할 수 없습니다

안전한 예외처리방법

체크 예외를 run() 메소드에서 던질 수 없도록 강제함으로 개발자는 체크 예외를 반드시
try-catch 블록 내에서 처리하게 됩니다

이것은 예상치 못한 예외로 인해 프로그램이 비정상 종료되는 것을 방지하며
특히 멀티스레딩 환경에서는 예외처리를 강제해서 스레드의 일관성와 안정성을 유지할 수 있습니다

최근에는 try-catch 블록에서 잡아서 RuntimeException으로 주로 던지곤합니다

void run(){
	try{
		Thread.sleep(5000);
	} catch (InterruptedException e){
		throw new RuntimeException(e);
	}

}

참고

  • 김영한의 실전 자바 - 고급 1편
profile
Software Developer

0개의 댓글