일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 |
- java network
- 도커 엔진
- 자바 io 보조스트림
- LIST
- Kubernetes
- 알고리즘
- 실전 자바 고급 1편
- 쿠버네티스
- Collection
- 리스트
- 도커
- 쓰레드
- 인프런
- 김영한
- 멀티 쓰레드
- container
- 자료구조
- 시작하세요 도커 & 쿠버네티스
- 동시성
- filewriter filereader
- java socket
- Thread
- Docker
- 자바
- Java IO
- 스레드
- java
- 컨테이너
- 자바 입출력 스트림
- 스레드 제어와 생명 주기
- Today
- Total
쌩로그
[JAVA] 김영한의 실전 자바 고급 1편 - Se03. 스레드 생성과 실행 본문
목차
- 포스팅 개요
- 본론
2-1. 스레드 시작1
2-2. 스레드 시작2
2-3. 데몬 스레드
2-4. 스레드 생성 - Runnable
2-5. 로거 만들기
2-6. 여러 스레드 만들기
2-7. Runnable을 만드는 다양한 방법 - 요약
1. 포스팅 개요
해당 포스팅은 김영한의 실전 자바 고급 1편 Section 3의 스레드 생성과 실행
에 대한 학습 내용이다.
참고로 프로젝스 환경 구성 부분은 제외한다.
그냥 프로젝트 만들면 됨.
학습 레포 URL : https://github.com/SsangSoo/inflearn-holyeye-java-adv1 (해당 레포는 완강시 public으로 전환 예정이다.)
2. 본론
2-1. 스레드 시작1
메모리 구조에서 Stack 영역이 있었다.
Stack 영역은 정확히 각 스레드별로 하나의 실행 스택이 생성된다.
따라서 스레드 수 만큼 Stack 이 생성 된다.
지금은 싱글 스레드이므로 Stack도 하나이다.
이후 스레드를 추가할 것인데, 그러면 Stack 도 스레드 수 만큼 증가한다.
스레드 생성
스레드를 직접 만들어본다. 그래서 해당 스레드에서 별도의 로직을 수행해보자.
스레드를 만들 때는 Thread
클래스를 상속 받는 방법과 Runnable
인터페이스를 구현하는 방법이 있다.
먼저 Thread
클래스를 상속 받아서 스레드를 생성해보자.
스레드 생성 - Thread 상속
자바는 많은 것을 객체로 다룬다. 자바가 예외를 객체로 다루듯이, 스레드도 객체로 다룬다.
스레드가 필요하면, 스레드 객체를 생성해서 사용하면 된다.
public class HelloThread extends Thread {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + ": run()");
}
}
Thread
클래스를 상속하고, 스레드가 실행할 코드를run()
메서드에 재정의한다.Thread.currentThread()
를 호출하면 해당 코드를 실행하는 스레드 객체를 조회할 수 있다.Thread.currentThread().getName()
: 실행 중인 스레드의 이름을 조회한다.
실행 예시다.
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");
}
}
// 결과
// 참고로 실행 결과는 스레드의 실행 순서에 따라 약간 다를 수 있다.
//이 부분은 바로 뒤에서 설명한다.
main: main() start
main: start 호출 전
main: start 호출 후
main: main() end
Thread-0: run()
- 앞서 만든
HelloThread
스레드 객체를 생성하고start()
메서드를 호출한다. start()
메서드는 스레드를 실행하는 아주 특별한 메서드이다.start()
를 호출하면HelloThread
스레드가run()
메서드를 실행한다.
주의!
run()
메서드가 아니라 반드시 start()
메서드를 호출해야 한다.
그래야 별도의 스레드에서 run()
코드가 실행된다.
스레드 생성 전

- 실행 결과를 보면
main()
메서드는main
이라는 이름의 스레드가 실행하는 것을 확인할 수 있다. - 프로세스가 작동하려면 스레드가 최소한 하나는 있어야 한다. 그래야 코드를 실행할 수 있다.
- 자바는 실행 시점에
main
이라는 이름의 스레드를 만들고 프로그램의 시작점인main()
메서드를 실행한다.
스레드 생성 후

HelloThread
스레드 객체를 생성한 다음에start()
메서드를 호출하면 자바는 스레드를 위한 별도의 스택 공간을 할당한다.- 스레드 객체를 생성하고, 반드시
start()
를 호출해야 스택 공간을 할당 받고 스레드가 작동한다. - 스레드에 이름을 주지 않으면 자바는 스레드에
Thread-0
,Thread-1
과 같은 임의의 이름을 부여한다. - 새로운
Thread-0
스레드가 사용할 전용 스택 공간이 마련되었다. Thread-0
스레드는run()
메서드의 스택 프레임을 스택에 올리면서run()
메서드를 시작한다.
메서드를 실행하면 스택 위에 스택 프레임이 쌓인다
main
스레드는main()
메서드의 스택 프레임을 스택에 올리면서 시작한다.- 직접 만드는 스레드는
run()
메서드의 스택 프레임을 스택에 올리면서 시작한다.
실행 결과를 보면 Thread-0
스레드가 run()
메서드를 실행한 것을 확인할 수 있다.
시간의 흐름으로 분석

main
스레드가HelloThread
인스턴스를 생성한다. 이때 스레드에 이름을 부여하지 않으면 자바가Thread-0
,Thread-1
과 같은 임의의 이름을 부여한다.start()
메서드를 호출하면,Thread-0
스레드가 시작되면서Thread-0
스레드가run()
메서드를 호출한다.- 여기서 핵심은
main
스레드가run()
메서드를 실행하는게 아니라Thread-0
스레드가run()
메서드를 실행한다는 점이다. main
스레드는 단지start()
메서드를 통해Thread-0
스레드에게 실행을 지시할 뿐이다.- 다시 강조하지만
main
스레드가run()
을 호출하는 것이 아니다! main
스레드는 다른 스레드에게 일을 시작하라고 지시만 하고, 바로start()
메서드를 빠져나온다.- 빠져나온다 -> 다음 라인의 코드를 실행한다는 말이다.
- 다시 강조하지만
- 이제
main
스레드와Thread-0
스레드는 동시에 실행된다. main
스레드 입장에서 보면 그림의 1, 2, 3번 코드를 멈추지 않고 계속 수행한다.- 그리고
run()
메서드는main
이 아닌 별도의 스레드에서 실행된다.
- 그리고
스레드 간 실행 순서는 보장하지 않는다.
스레드는 동시에 실행되기 때문에 스레드 간에 실행 순서는 얼마든지 달라질 수 있다. 따라서 다음과 같이 다양한 실행 결과가 나올 수 있다.
main
스레드가 빨리 실행된 경우
main: main() start
main: start() 호출 전
main: start() 호출 후
main: main() end
Thread-0: run()
main
스레드가 모든 로직을 다 수행한 다음에 Thread-0
가 수행된다.
Thread-0
스레드가 빨리 실행된 경우
main: main() start
main: start() 호출 전
Thread-0: run()
main: start() 호출 후
main: main() end
Thread-0
start()
이후 Thread-0
가 먼저 수행되고, main
이 수행된다.
main
스레드 실행 중간에 Thread-0
스레드가 실행된 경우
main: main() thread.start
main: start() 호출 전
main: start() 호출 후
Thread-0: run()
main: main() end
main 스레드가 "start() 호출 후"를 출력한 다음에 Thread-0
스레드 run()
을 출력한다.
- 스레드 간의 실행 순서는 얼마든지 달라질 수 있다.
- CPU 코어가 2개여서 물리적으로 정말 동시에 실행될 수도 있고, 하나의 CPU 코어에 시간을 나누어 실행될 수도 있다.
- 한 스레드가 얼마나 오랜기간 실행되는지도 보장하지 않는다.
- 한 스레드가 먼저 다 수행된 다음에 다른 스레드가 수행될 수도 있고, 둘이 완전히 번갈아 가면서 수행되는 경우도 있다.
스레드는 순서와 실행 기간을 모두 보장하지 않는다.
이것이 바로 멀티스레드다.
2-2. 스레드 시작2
start() vs run()
스레드의 start()
대신에 재정의한 run()
메서드를 직접 호출하면 어떻게 될까?
public class BadThreadMain {
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.run();
System.out.println(Thread.currentThread().getName() + ": start 호출 후");
System.out.println(Thread.currentThread().getName() + ": main() end");
}
}
// 실행 결과
main: main() start
main: start 호출 전
main: run()
main: start 호출 후
main: main() end
helloThread.run()
:start()
를 호출해야 하지만 여기서는 문제를 확인하기 위해run()
을 직접 호출한다.
run() 직접 호출

- 실행 결과를 잘 보면 별도의 스레드가
run()
을 실행하는 것이 아니라,main
스레드가run()
메서드를 호출할 것을 확인할 수 있다. - 자바를 처음 실행하면
main
스레드가main()
메서드를 호출하면서 시작한다. main
스레드는HelloThread
인스턴스에 있는run()
이라는 메서드를 호출한다.main
스레드가run()
메서드를 실행했기 때문에main
스레드가 사용하는 스택위에run()
스택 프레임이 올라간다.

결과적으로 main
스레드에서 모든 것을 처리한 것이 된다.
스레드의 start()
메서드는 스레드에 스택 공간을 할당하면서 스레드를 시작하는 아주 특별한 메서드이다.
그리고 해당 스레드에서 run()
메서드를 실행한다.
따라서 main
스레드가 아닌 별도의 스레드에서 재정의한 run()
메서드를 실행하려면, 반드시 start()
메서드를 호출해야 한다.
참고: 스레드와 메모리 구조에 대한 부분은 강의를 진행하면서 점점 더 자세히 설명한다.
2-3. 데몬 스레드
데몬 스레드
스레드는 사용자(user) 스레드와 데몬(daemon) 스레드 2가지 종류로 구분할 수 있다.
사용자 스레드(non-daemon 스레드)
- 프로그램의 주요 작업을 수행한다.
- 작업이 완료될 때까지 실행된다.
- 모든 user 스레드가 종료되면 JVM도 종료된다.
데몬 스레드
- 백그라운드에서 보조적인 작업을 수행한다.
- 모든 user 스레드가 종료되면 데몬 스레드는 자동으로 종료된다.
JVM은 데몬 스레드의 실행 완료를 기다리지 않고 종료된다.
데몬 스레드가 아닌 모든 스레드가 종료되면, 자바 프로그램도 종료된다.
용어 - 데몬
그리스 신화에서 데몬은 신과 인간 사이의 중간적 존재로, 보이지 않게 활동하며 일상적인 일들을 도왔다.
이런 의미로 컴퓨터 과학에서는 사용자에게 직접적으로 보이지 않으면서 시스템의 백그라운드에서 작업을 수행하는 것을 데몬 스레드, 데몬 프로세스라 한다.
예를 들어서 사용하지 않는 파일이나 메모리를 정리하는 작업들이 있다.
public class DaemonThreadMain {
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName() + ": main() start");
DaemonThread daemonThread = new DaemonThread();
daemonThread.setDaemon(true); // 데몬 스레드 여부
daemonThread.start();
System.out.println(Thread.currentThread().getName() + ": main() end");
}
static class DaemonThread extends Thread {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + ": run()");
try {
Thread.sleep(10000); // 10초간 실행
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName() + ": run() end");
}
}
}
// 실행 결과
main: main() start
main: main() end
Thread-0: run()
setDaemon(true)
: 데몬 스레드로 설정한다.- 데몬 스레드 여부는
start()
실행 전에 결정해야 한다. 이후에는 변경되지 않는다. - 기본 값은
false
이다. (user 스레드가 기본)
참고run()
메서드 안에서 Thread.sleep()
를 호출할 때 체크 예외인 InterruptedException
을 밖으로 던질 수 없고 반드시 잡아야 한다.run()
메서드는 체크 예외를 밖으로 던질 수 없는데, 이 부분은 뒤에서 설명한다.
실행결과 - setDaemon(true)
main: main() start
main: main() end
Thread-0: run()
setDaemon(true)
로 설정해보자.Thread-0
는 데몬 스레드로 설정된다.- 유일한
user
스레드인main
스레드가 종료되면서 자바 프로그램도 종료된다.- 따라서
run() end
가 출력되기 전에 프로그램이 종료된다.
- 따라서
실행결과 - setDaemon(false)
main: main() start
main: main() end
Thread-0: run()
Thread-0: run() end
setDaemon(false)
로 설정해보자.Thread-0
는 user 스레드로 설정된다.main
스레드가 종료되어도,user
스레드인Thread-0
가 종료될 때까지 자바 프로그램은 종료되지 않는다.- 따라서
Thread-0
:run() end
가 출력된다. user
스레드인main
스레드와Thread-0
스레드가 모두 종료되면서 자바 프로그램도 종료된다.
2-4. 스레드 생성 - Runnable
스레드를 만들 때는 Thread
클래스를 상속 받는 방법과 Runnable
인터페이스를 구현하는 방법이 있다.
앞서 Thread
클래스를 상속 받아서 스레드를 생성해보았다.
이번에는 Runnable
인터페이스를 구현하는 방식으로 스레드를 생성해보자.
참고로 실무에서는 대부분 이 방법을 사용한다.
Runnable 인터페이스
package java.lang;
@FunctionalInterface
public interface Runnable {
void run();
}
- 자바가 제공하는 스레드 실행용 인터페이스
public class HelloRunnable implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + ": run()");
}
}
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");
}
}
// 실행 결과
main: main() start
main: main() end
Thread-0: run()
실행 결과는 기존과 같다.
차이가 있다면, 스레드와 해당 스레드가 실행할 작업이 서로 분리되어 있다는 점이다.
스레드 객체를 생성할 때, 실행할 작업을 생성자로 전달하면 된다.
Thread 상속 vs Runnable 구현
스레드 사용할 때는 Thread
를 상속 받는 방법보다 Runnable
인터페이스를 구현하는 방식을 사용하자.
두 방식이 서로 장단점이 있지만, 스레드를 생성할 때는 Thread
클래스를 상속하는 방식보다 Runnable
인터페이스를 구현하는 방식이 더 나은 선택이다.
Thread 클래스 상속 방식
장점
- 간단한 구현:
Thread
클래스를 상속받아run()
메서드만 재정의하면 된다. - 단점*
- 상속의 제한: 자바는 단일 상속만을 허용하므로 이미 다른 클래스를 상속받고 있는 경우
Thread
클래스를 상속 받을 수 없다. - 유연성 부족: 인터페이스를 사용하는 방법에 비해 유연성이 떨어진다.
Runnable 인터페이스를 구현 방식
장점
- 상속의 자유로움:
Runnable
인터페이스 방식은 다른 클래스를 상속받아도 문제 없이 구현할 수 있다. - 코드의 분리: 스레드와 실행할 작업을 분리하여 코드의 가독성을 높일 수 있다.
- 여러 스레드가 동일한
Runnable
객체를 공유할 수 있어 자원 관리를 효율적으로 할 수 있다. - 단점*
- 코드가 약간 복잡해질 수 있다.
Runnable
객체를 생성하고 이를Thread
에 전달하는 과정이 추가된다.
정리하자면 Runnable
인터페이스를 구현하는 방식을 사용하자.
스레드와 실행할 작업을 명확히 분리하고, 인터페이스를 사용하므로 Thread
클래스를 직접 상속하는 방식보다 더 유연하고 유지보수 하기 쉬운 코드를 만들 수 있다.
2-5. 로거 만들기
현재 어떤 스레드가 코드를 실행하는지 출력하기 위해 다음과 같이 긴 코드를 작성하는 것은 너무 번거롭다.
System.out.println(Thread.currentThread().getName() + ": run()");
다음 예시와 같이 실행하면, 현재 시간, 스레드 이름, 출력 내용등이 한번에 나오는 편리한 기능을 만들어보자.
log("hello thread");
log(123);
실행 결과
15:39:02.000 [ main] hello thread
15:39:02.000 [ main] 123
package util;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
public abstract class MyLogger {
private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm:ss.SSS");
public static void log(Object obj) {
String time = LocalTime.now().format(formatter);
System.out.printf("%s [%9s] %s\n", time, Thread.currentThread().getName(), obj);
}
}
util
이라는 패키지를 사용했다. 프로젝트 전반에 사용되는 유틸리티라는 뜻이다.- 현재 시간을 원하는 포멧으로 출력하기 위해
DateTimeFormatter
를 사용한다. printf
에서%s
는 문자열을 뜻한다. 인자를 순서대로 사용한다.- 여기서는 현재 시간, 스레드 이름, 출력할 객체 순서이다.
- 참고로 마지막의 출력할 객체는 문자열이 아니라
Object
타입인데,%s
를 사용하면toString()
을 사용해서 문자열로 변환 후 출력한다. - 이렇게
Object
타입을 사용하면 문자열 뿐만 아니라 객체도 출력할 수 있다.
%9s
는 다음과 같이 문자를 출력할 때 9칸을 확보한다는 뜻이다.- 9칸이 차지 않으면 왼쪽에 그 만큼 비워둔다. 이 기능은 단순히 출력시 정렬을 깔끔하게 하려고 사용한다.
- 예)
[ main]
: 앞에 5칸 공백[ Thread-0]
: 앞에 1칸 공백
package util;
import static util.MyLogger.*;
public class MyLoggerMain {
public static void main(String[] args) {
log("hello thread");
log(123);
}
}
// 실행 결과
05:57:15.763 [ main] hello thread
05:57:15.764 [ main] 123
- 사용할 때는 지금과 같이
import static
을 사용하면 메서드 이름만으로 간단히 사용할 수 있다.
스레드를 학습할 때는 스레드 이름, 그리고 해당 스레드가 언제 실행되었는지 확인하는 것이 중요하다.
앞으로는 System.out.println()
대신에 스레드 이름과 실행 시간을 알려주는 MyLogger
를 사용하겠다.
2-6. 여러 스레드 만들기
이번에는 많은 스레드를 어떻게 한 번에 만드는지 알아보자.
단순히 스레드 3개를 생성하고 실행해보자.
import static util.MyLogger.*;
public class ManyThreadMainV1 {
public static void main(String[] args) {
log("main() start");
HelloRunnable runnable = new HelloRunnable();
Thread thread1 = new Thread(runnable);
thread1.start();
Thread thread2 = new Thread(runnable);
thread2.start();
Thread thread3 = new Thread(runnable);
thread3.start();
log("main() end");
}
}
// 실행 결과
06:00:18.524 [ main] main() start
06:00:18.527 [ main] main() end
Thread-1: run()
Thread-2: run()
Thread-0: run()
- 실행 결과는 다를 수 있다. 스레드의 실행 순서는 보장되지 않는다.

- 스레드3개를 생성할 때 모두 같은
HelloRunnable
인스턴스(x001
)를 스레드의 실행 작업으로 전달했다. Thread-0
,Thread-1
,Thread-2
는 모두HelloRunnable
인스턴스에 있는run()
메서드를 실행한다.
스레드 100개를 생성하고 실행해보자.
import static util.MyLogger.log;
public class ManyThreadMainV2 {
public static void main(String[] args) {
log("main() start");
HelloRunnable runnable = new HelloRunnable();
for(int i = 0; i < 100; i++) {
Thread thread = new Thread(runnable);
thread.start();
}
log("main() end");
}
}
// 실행 결과
06:02:19.671 [ main] main() start
Thread-0: run()
Thread-1: run()
Thread-6: run()
Thread-2: run()
Thread-5: run()
Thread-9: run()
Thread-4: run()
Thread-13: run()
Thread-7: run()
Thread-3: run()
Thread-12: run()
Thread-10: run()
Thread-17: run()
Thread-11: run()
Thread-14: run()
Thread-15: run()
Thread-16: run()
Thread-21: run()
Thread-18: run()
Thread-19: run()
Thread-8: run()
Thread-20: run()
Thread-22: run()
Thread-24: run()
Thread-25: run()
Thread-23: run()
Thread-26: run()
Thread-27: run()
Thread-28: run()
Thread-29: run()
Thread-30: run()
Thread-31: run()
Thread-32: run()
Thread-33: run()
Thread-34: run()
Thread-35: run()
Thread-37: run()
Thread-38: run()
Thread-36: run()
Thread-40: run()
Thread-39: run()
Thread-41: run()
Thread-42: run()
Thread-45: run()
Thread-46: run()
Thread-44: run()
Thread-49: run()
Thread-50: run()
Thread-51: run()
Thread-48: run()
Thread-43: run()
Thread-53: run()
Thread-47: run()
Thread-54: run()
Thread-52: run()
Thread-55: run()
Thread-56: run()
Thread-57: run()
Thread-58: run()
Thread-59: run()
Thread-60: run()
Thread-61: run()
Thread-62: run()
Thread-63: run()
Thread-64: run()
Thread-65: run()
Thread-66: run()
Thread-67: run()
Thread-68: run()
Thread-69: run()
Thread-70: run()
Thread-71: run()
Thread-72: run()
Thread-73: run()
Thread-74: run()
Thread-75: run()
Thread-76: run()
Thread-77: run()
Thread-78: run()
Thread-79: run()
Thread-80: run()
Thread-81: run()
Thread-82: run()
Thread-83: run()
Thread-84: run()
Thread-85: run()
Thread-86: run()
Thread-87: run()
Thread-88: run()
Thread-89: run()
Thread-90: run()
Thread-91: run()
Thread-92: run()
Thread-94: run()
Thread-95: run()
Thread-93: run()
Thread-96: run()
06:02:19.689 [ main] main() end
Thread-98: run()
Thread-97: run()
Thread-99: run()
- 반복문을 사용하면 스레드의 숫자를 유동적으로 변경하면서 실행할 수 있다.
- 실행 결과는 늘 다를 수 있다.
- 스레드의 실행 순서는 보장되지 않는다.
2-7. Runnable을 만드는 다양한 방법
중첩 클래스를 사용하면 Runnable 을 더 편리하게 만들 수 있다.
(중첩 클래스는 자바 중급1편 참고)
참고로 모두 결과는 같다.
정적 중첩 클래스 사용
import static util.MyLogger.log;
public class InnerRunnableMainV1 {
public static void main(String[] args) {
log("main() start");
Runnable runnable = new MyRunnable();
Thread thread = new Thread(runnable);
thread.start();
log("main() end");
}
static class MyRunnable implements Runnable {
@Override
public void run() {
log("run()");
}
}
}
// 실행 결과
06:06:05.030 [ main] main() start
06:06:05.043 [ main] main() end
06:06:05.043 [ Thread-0] run()
- 특정 클래스 안에서만 사용되는 경우 이렇게 중첩 클래스를 사용하면 된다.
익명 클래스 사용
import static util.MyLogger.log;
public class InnerRunnableMainV2 {
public static void main(String[] args) {
log("main() start");
Runnable runnable = new Runnable() {
@Override
public void run() {
log("run()");
}
};
Thread thread = new Thread(runnable);
thread.start();
log("main() end");
}
}
// 실행 결과
06:07:02.571 [ main] main() start
06:07:02.585 [ main] main() end
06:07:02.585 [ Thread-0] run()
- 특정 메서드 안에서만 간단히 정의하고 사용하고 싶다면 익명 클래스를 사용하면 된다.
익명 클래스 변수 없이 직접 전달
import static util.MyLogger.log;
public class InnerRunnableMainV3 {
public static void main(String[] args) {
log("main() start");
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
log("run()");
}
});
thread.start();
log("main() end");
}
}
// 실행 결과
06:08:10.898 [ main] main() start
06:08:10.914 [ main] main() end
06:08:10.914 [ Thread-0] run()
- 익명 클래스를 참조하는 변수를 만들지 않고 직접 전달할 수 있다.
람다
import static util.MyLogger.log;
public class InnerRunnableMainV4 {
public static void main(String[] args) {
log("main() start");
Thread thread = new Thread(() -> log("run()"));
thread.start();
log("main() end");
}
}
// 실행 결과
06:08:48.777 [ main] main() start
06:08:48.781 [ main] main() end
06:08:48.781 [ Thread-0] run()
- 람다를 사용하면 메서드(함수) 코드 조각을 전달할 수 있다.
- 우리는 아직 람다를 학습하지 않았기 때문에 정적 중첩 클래스나 익명 클래스를 주로 사용하겠다.
- 참고로 람다는 별도의 강의에서 다룰 예정이다.
3. 요약
쓰레드를 생성하고, 어떻게 실행하는지 살펴봤다.
- 쓰레드를 생성할 때는 Runnable 인터페이스를 구현하고,
run()
메서드가 아니라,start()
메서드를 호출해야main
이 아닌 다른 스레드에 의해서 Stack이 만들어지고, 그 Stack 영역에 생성된 스레드가 하는 일이 수행된다.
'Language > JAVA' 카테고리의 다른 글
[JAVA] 김영한의 실전 자바 고급 1편 - Se05. 스레드 제어와 생명 주기2 (0) | 2025.03.16 |
---|---|
[JAVA] 김영한의 실전 자바 고급 1편 - Se04. 스레드 제어와 생명 주기1 (0) | 2025.03.07 |
[JAVA] 김영한의 실전 자바 고급 1편 - Se02. 프로세스와 스레드 소개 (2) | 2025.02.05 |
김영한의 실전 자바 - 중급 2편 - Sec 11. 컬렉션 프레임워크 - 순회, 정렬, 전체 정리 (1) | 2025.02.03 |
김영한의 실전 자바 - 중급 2편 - Sec 10. 컬렉션 프레임워크 - Map, Stack, Queue (1) | 2025.01.29 |