자바에서 클래스 안에 또 클래스를 정의할 수 있고 이것이 중첩 클래스(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
이 방식은 모든 스레드에게 실행 기회를 공평하게 나눠주는 특징이있다