자바에서 클래스 안에 또 클래스를 정의할 수 있고 이것이 중첩 클래스(Nested Class)이다.
static
이 없는 클래스. 바깥 클래스의 인스턴스에 접근 가능static
이 붙은 클래스. 바깥 클래스의 인스턴스 없이 사용 가능class Outer {
class Inner { } // 인스턴스 멤버 클래스
static class StaticInner { } // 정적 멤버 클래스
}
void method() {
class LocalClass {
void sayHi() {
System.out.println("Hi");
}
}
new LocalClass().sayHi();
}
.class
파일로 저장될 때 Outer$1.class
처럼 저장
Runnable r = new Runnable() {
public void run() {
System.out.println("익명 클래스!");
}
};
new 인터페이스명(){}
← 이름 없는 클래스new 부모클래스명(){}
← 자식 클래스를 즉석에서 생성.java
파일로는 못 만들고, .class
파일은 $숫자
로 자동 저장Thread
클래스를 상속class MyThread extends Thread {
public void run() {
System.out.println("스레드 작업 중!");
}
}
MyThread t = new MyThread();
t.start(); // 새로운 스레드 시작
Runnable
인터페이스 구현class MyRunnable implements Runnable {
public void run() {
System.out.println("Runnable 작업 중!");
}
}
Thread t = new Thread(new MyRunnable());
t.start();
Thread
클래스 내부에 정의된 메서드Thread
클래스를 상속받아 오버라이딩하여 사용하며, 스레드가 수행할 작업을 정의하는 데 쓴다class MyThread extends Thread {
@Override
public void run() {
System.out.println("작업 실행 중");
}
}
new MyThread().run();
처럼 호출하면, 새로운 스레드가 만들어지지 않고 main
스레드에서 그대로 실행됨start()
는 Thread
클래스에 정의된 메서드, 새로운 스레드를 생성하고 run()
을 자동으로 실행run()
을 호출하면 안 되고, 반드시 start()
를 통해 스레드를 시작해야 멀티스레딩 효과를 볼 수 있음MyThread t1 = new MyThread();
t1.start(); // 새로운 스레드가 만들어지고, 내부적으로 run()이 호출된다.
join()
은 다른 스레드의 작업이 끝날 때까지 현재 스레드를 대기시키는 메서드다Thread t1 = new MyThread();
Thread t2 = new MyThread();
t1.start();
t2.start();
t1.join(); // t1이 끝날 때까지 기다림
t2.join(); // t2가 끝날 때까지 기다림
System.out.println("모든 작업 완료");
join()
을 사용하지 않으면, main
스레드가 먼저 종료되어 프로그램이 의도치 않게 동작할 수 있음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()
의 차이run()
은 그냥 메서드 실행run()
을 직접 호출하면 새로운 스레드가 만들어지지 않음run()
메서드 내용이 실행됨MyThread t = new MyThread();
t.run(); // ❌ 그냥 메서드 호출일 뿐
start()
는 새로운 스레드를 만들어서 run()
실행start()
를 호출하면 JVM이 새로운 스레드를 만들어 줌run()
을 실행MyThread t = new MyThread();
t.start(); // ✅ 새 스레드에서 run() 실행
public void throwException() throws Exception {
// ❌ 이 메서드 안에서 throw new Exception(); 하면
// 스레드 내부 예외는 반드시 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 |
예시:
- 스레드 A → 0.1초 실행 → 스레드 B → 0.1초 실행 → 스레드 C → ... → 다시 스레드 A
이 방식은 모든 스레드에게 실행 기회를 공평하게 나눠주는 특징이있다