중첩 클래스(Nested Class)

Jean·2025년 8월 6일
0

풀스택 교육

목록 보기
24/28

자바에서 클래스 안에 또 클래스를 정의할 수 있고 이것이 중첩 클래스(Nested Class)이다.

✅ 1. 멤버 클래스 (Member Class)

  • 인스턴스 클래스: static이 없는 클래스. 바깥 클래스의 인스턴스에 접근 가능
  • 스태틱 클래스 (정적 클래스): static이 붙은 클래스. 바깥 클래스의 인스턴스 없이 사용 가능
class Outer {
    class Inner { }       // 인스턴스 멤버 클래스
    static class StaticInner { } // 정적 멤버 클래스
}

✅ 2. 로컬 클래스 (Local Class)

  • 메서드 안에서 선언되는 클래스. 해당 메서드 안에서만 사용 가능
  • 자바는 호이스팅 없음 → 정의보다 위에서 사용 ❌
void method() {
    class LocalClass {
        void sayHi() {
            System.out.println("Hi");
        }
    }
    new LocalClass().sayHi();
}

✅ 3. 익명 이너 클래스 (Anonymous Inner Class)

  • 이름 없이 바로 정의하는 클래스
  • 주로 인터페이스나 추상 클래스의 인스턴스를 바로 만들 때 사용
  • 자바에서 .class 파일로 저장될 때 Outer$1.class 처럼 저장

Runnable r = new Runnable() {
    public void run() {
        System.out.println("익명 클래스!");
    }
};
  • new 인터페이스명(){} ← 이름 없는 클래스
  • new 부모클래스명(){} ← 자식 클래스를 즉석에서 생성
  • 이름이 없어서 .java 파일로는 못 만들고, .class 파일은 $숫자로 자동 저장

⚙️ 멀티 스레딩

💡 기본 개념

  • 프로세스: 실행 중인 프로그램 (ex: 크롬, 메모장)
  • 스레드: 하나의 프로세스 안에서 독립적으로 실행되는 작업 단위
  • 멀티스레드: 하나의 프로그램 안에서 여러 작업을 동시에 실행
  • 멀티태스킹: 여러 프로그램이 동시에 돌아가는 것 (멀티프로세싱 + 멀티스레딩)

🔨 자바에서 스레드 만들기

📌 방법 1: Thread 클래스를 상속

class MyThread extends Thread {
    public void run() {
        System.out.println("스레드 작업 중!");
    }
}
MyThread t = new MyThread();
t.start();  // 새로운 스레드 시작

📌 방법 2: Runnable 인터페이스 구현

class MyRunnable implements Runnable {
    public void run() {
        System.out.println("Runnable 작업 중!");
    }
}
Thread t = new Thread(new MyRunnable());
t.start();

🧵 Thread 메서드들

✅ run() 메서드

  • Thread 클래스 내부에 정의된 메서드
  • 직접 호출하면 새로운 스레드가 생성되지 않고, 일반 메서드처럼 동일한 스레드에서 실행됨
  • 보통 Thread 클래스를 상속받아 오버라이딩하여 사용하며, 스레드가 수행할 작업을 정의하는 데 쓴다
class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("작업 실행 중");
    }
}
  • new MyThread().run();처럼 호출하면, 새로운 스레드가 만들어지지 않고 main 스레드에서 그대로 실행됨

✅ start() 메서드

  • start()Thread 클래스에 정의된 메서드, 새로운 스레드를 생성하고 run()자동으로 실행
  • 사용자가 직접 run()을 호출하면 안 되고, 반드시 start()를 통해 스레드를 시작해야 멀티스레딩 효과를 볼 수 있음
MyThread t1 = new MyThread();
t1.start(); // 새로운 스레드가 만들어지고, 내부적으로 run()이 호출된다.

✅ join() 메서드

  • join()다른 스레드의 작업이 끝날 때까지 현재 스레드를 대기시키는 메서드다
  • 여러 스레드가 병렬로 실행될 때, 모든 작업이 끝난 뒤 다음 작업을 진행하고 싶을 때 사용된다.
Thread t1 = new MyThread();
Thread t2 = new MyThread();

t1.start();
t2.start();

t1.join(); // t1이 끝날 때까지 기다림
t2.join(); // t2가 끝날 때까지 기다림

System.out.println("모든 작업 완료");
  • 위 코드에서 join()을 사용하지 않으면, main 스레드가 먼저 종료되어 프로그램이 의도치 않게 동작할 수 있음

🔍 run() 은 왜 오버라이딩하는 걸까?

  • Thread 클래스는 기본적으로 아무 작업도 하지 않는 run()을 갖고 있음
  • 우리가 하고 싶은 작업을 넣기 위해 반드시 오버라이딩해야 한다
  • 또는 Runnable 인터페이스를 구현하고 run()을 정의한 뒤 Thread 생성자에 전달하는 것도 가능
Runnable task = new Runnable() {
    @Override
    public void run() {
        System.out.println("Runnable로 작업 실행");
    }
};

Thread t = new Thread(task);
t.start();

🧩 요약

  • run() → 스레드 생성 없이 실행, 작업 정의용 (오버라이딩 대상)
  • start() → 스레드 생성 + 내부적으로 run() 실행
  • join() → 다른 스레드가 끝날 때까지 대기

🥊 run()start()의 차이

1. run()은 그냥 메서드 실행

  • run()을 직접 호출하면 새로운 스레드가 만들어지지 않음
  • 그냥 현재 실행 중인 스레드에서 run() 메서드 내용이 실행됨
MyThread t = new MyThread();
t.run(); // ❌ 그냥 메서드 호출일 뿐

2. start()는 새로운 스레드를 만들어서 run() 실행

  • start()를 호출하면 JVM이 새로운 스레드를 만들어
  • 그 새 스레드가 내부적으로 자동으로 run()을 실행
MyThread t = new MyThread();
t.start(); // ✅ 새 스레드에서 run() 실행

🧪 예외 처리에서 주의

public void throwException() throws Exception {
    // ❌ 이 메서드 안에서 throw new Exception(); 하면
    // 스레드 내부 예외는 반드시 try-catch로 감싸야 함
}
  • 스레드 안에서는 예외를 밖으로 던져도 (throws) 호출하는 쪽에서 catch하지 않으면 처리 안 됨
  • 그래서 스레드 안에서는 무조건 try-catch로 감싸는 게 안전함

🌿 람다식으로 해보자

Runnable r = () -> System.out.println("람다 스레드 실행!");
new Thread(r).start();
  • 익명 이너 클래스를 간결하게 쓰는 방법
  • Runnable함수형 인터페이스 → 람다로 표현 가능

🧶 스레드 상태

상태설명
New (생성)Thread t = new Thread(); 스레드 객체를 만들었지만 아직 시작하지 않은 상태
Runnable (실행 가능)t.start(); 실행 준비 완료! CPU 스케줄링을 기다리는 상태
Running (실행 중)CPU가 스레드를 선택해서 실제로 실행되는 상태
Blocked / Waiting / Timed Waiting (일시 정지)어떤 이유로 잠시 멈춘 상태 (예: IO 기다리거나 sleep, join)
Terminated (종료)작업을 끝냈거나 오류가 발생해서 스레드가 종료된 상태
  • 스레드는 생성 → 실행 → 종료의 흐름을 갖고, 중간에 일시정지 상태가 있을 수 있다
  • 운영체제는 여러 스레드를 라운드 로빈 방식으로 번갈아가며 실행
  • 여러 작업이 동시에 되는 것처럼 보이게 된다.

🛑 일시정지 상태?

스레드가 실행 중이거나 대기 중이었다가 일시적으로 멈추는 상태

  • sleep(1000) : 자는 중…
  • join() : 다른 스레드 끝날 때까지 기다림
  • wait() : 누군가 깨울 때까지 기다림

📝 일시정지 상태는 스레드가 자발적으로 멈추거나 외부 요청에 의해 멈추는 경우다 (CPU를 사용하지 않음)

🌟 스레드의 상태 메서드

기능설명상태 변화
start()스레드 실행 준비New → Runnable
sleep()일정 시간 잠들기Running → Timed Waiting
yield()CPU 잠깐 양보Running → Runnable
join()다른 스레드 끝날 때까지 기다리기Running → Waiting
wait()공유 객체의 락 해제 후 기다림Running → Waiting
interrupt()스레드 깨우거나 플래그 설정Waiting → Runnable 등
stop()강제 종료 (지양)모든 상태 → Terminated

🌀 라운드 로빈(Round-Robin) 방식

  • 여러 스레드가 있을 때, 공평하게 돌아가면서 CPU를 사용하는 방식
  • 각 스레드는 짧은 시간 단위(Time Slice) 만큼만 실행하고, 다음 차례에게 넘겨준다

예시:

  • 스레드 A → 0.1초 실행 → 스레드 B → 0.1초 실행 → 스레드 C → ... → 다시 스레드 A

이 방식은 모든 스레드에게 실행 기회를 공평하게 나눠주는 특징이있다


profile
햇내기 개발자 지망생

0개의 댓글