자바 메모리 구조
자바 메모리 구조는 총 메서드 영역, 스택 영역, 힙 영역 이렇게 3가지 영역으로 구성되어 있다.

- 메서드 영역: 프로그램 공통 데이터 관리
- 클래스 정보: 클래스의 실행 코드(바이트 코드), 필드, 메서드, 생성자 코드 등 모든 실행코드
- static 영역:
static변수 보관 - 런타임 상수 풀: 공통 리터럴 상수 보관
- 스택 영역: 스레드별 하나의 실행 스택이 생성됨
- 스택 프레임: 메서드를 호출할 때마다 하나의 스택 프레임이 쌓이고, 종료되면 해당 스택 프레임이 제거됨
- 각 스택 프레임은
메서드를 실행하기 위한 정보(지역 변수, 중간 연산 결과, 메서드 호출 정보 등)를 포함
- 힙 영역: 객체(인스턴스)와 배열이 생성되는 영역
- 가비지 컬렉션(GC)이 이루어지는 주요 영역이며, 더 이상 참조되지 않는 객체는 GC에 의해 제거됨
스레드 생성 방법 1.Thread 클래스 상속
자바는 많은 것을 객체로 다룬다. 예외를 객체로 다루듯이, 스레드도 객체로 다룬다. 스레드가 필요하면, 스레드 객체를 생성해서 사용하면 된다.
package thread.start;
public class HelloThread extends Thread {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + ": run()");
}
}
Thread클래스를 상속하고, 스레드가 실행할 코드를run()메서드에 재정의한다
package thread.start;
public class HelloThreadMain {
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName() + ": main() start");
HelloThread helloThread = new HelloThread();
System.out.println(Thread.currentThread().getName() + ": start() 호출 전");
helloThread.start();
System.out.println(Thread.currentThread().getName() + ": start() 호출 후");
System.out.println(Thread.currentThread().getName() + ": main() end");
}
}
- 앞서 만든
HelloThread스레드 객체를 생성하고start()메서드를 호출한다 start()매서드는 스레드를 실행하는 아주 특별한 메서드이다start()를 호출하면HelloThread스레드가run()매서드를 실행한다run()메서드가 아니라 반드시start()메서드를 호출해야한다. 그래야 별도의 스레드에서run()코드가 실행된다
스레드 생성 후 메모리 분석

시간의 흐름으로 분석

- 여기서 핵심은
main스레드가run()메서드를 실행하는게 아니라Thread-0스레드가run()메서드를 실행한다는 점이다. main스레드는 단지start()메서드를 통해Thread-0스레드에게 실행을 지시할 뿐이다.main스레드가run()을 호출하는 것이 아니다!main스레드는 다른 스레드에게 일을 시작하라고 지시만 하고, 바로start()메서드를 빠져나온다.- 스레드 간 실행 순서는 보장하지 않는다. 스레드는 동시에 실행되기 때문에 스레드 간에 실행 순서는 얼마든지 달라질 수 있다.
스레드 생성 방법 2. Runnable 인터페이스 구현
package thread.start;
public class HelloRunnable implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + ": run()");
}
}
package thread.start;
public class HelloRunnableMain {
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName() + ": main() start");
HelloRunnable runnable = new HelloRunnable();
Thread thread = new Thread(runnable);
thread.start();
System.out.println(Thread.currentThread().getName() + ": main() end");
}
}
- 이전에 Thread 객체를 생성했을 때와 결과의 차이는 없다
- 차이가 있다면, 스레드와 해당 스레드가 실행할 작업이 서로 분리되어 있다는 점이다
- 스레드 객체를 생성할 때, 실행할 작업을 생성자로 전달하면 된다
Runnable 인터페이스 구현 방식의 장점
- 상속의 자유로움:
Runnable인터페이스 방식은 다른 클래스를 상속받아도 문제없이 구현할 수 있다 (Thread를 상속받은 경우 다중 상속은 불가함) - 코드의 분리: 스레드와 실행할 작업을 분리하여 코드의 가독성을 높일 수 있다
- 여러 스레드가 동일한
Runnable객체를 공유할 수 있어 자원 관리를 효율적으로 할 수 있다
위와 같은 이유들로 스레드를 생성하고 사용할 때 Runnable 인터페이스 구현 방식이 권장된다.