싱글 쓰레드 는 한 번에하나의 작업만 처리하지만, 멀티 쓰레드 는 여러 작업을 동시에 처리할 수 있음 (병렬 처리)멀티 쓰레드 를 활용하면 여러 작업을 병렬로 수행할 수 있어main() 메소드는 프로그램 시작과 동시에 생성되는 하나의 쓰레드public class Main {
public static void main(String[] args) {
System.out.println("::: main 쓰레드 시작 :::");
String threadName = Thread.currentThread().getName();
// ✅ 하나의 작업 단위: 숫자를 0 부터 9 까지 출력
for (int i = 0; i < 10; i++) {
System.out.println("현재 쓰레드: " + threadName + " - " + i);
try {
Thread.sleep(500); // 0.5 초 대기
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// ✅ 추가 작업 단위
for (int i = 0; i < 10; i++) {
System.out.println("현재 쓰레드: " + threadName + " - " + i);
try {
Thread.sleep(500); // 0.5 초 대기
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("::: 작업 끝 :::");
}
}
::: main 쓰레드 시작 :::
현재 쓰레드: main - 0
현재 쓰레드: main - 1
현재 쓰레드: main - 2
현재 쓰레드: main - 3
현재 쓰레드: main - 4
현재 쓰레드: main - 5
현재 쓰레드: main - 6
현재 쓰레드: main - 7
현재 쓰레드: main - 8
현재 쓰레드: main - 9
현재 쓰레드: main - 0
현재 쓰레드: main - 1
현재 쓰레드: main - 2
현재 쓰레드: main - 3
현재 쓰레드: main - 4
현재 쓰레드: main - 5
현재 쓰레드: main - 6
현재 쓰레드: main - 7
현재 쓰레드: main - 8
현재 쓰레드: main - 9
::: 작업 끝 :::
Thread 클래스를 상속받아 쓰레드를 구현할 수 있음Thread.run() 메소드를 오버라이드 해서 수행할 작업을 정의할 수 있습니다.start() 메소드를 호출하면 새로운 쓰레드가 생성되고 run() 의 작업 내용이 실행됩니다.main, thread0, thread1) 병렬로 실행됩니다.main 쓰레드는 thread0, thread1 을 생성하고 실행시킵니다.thread0, thread1 는 0.5초마다 0 ~ 9까지 숫자를 출력합니다.⚠️ 쓰레드를 실행시킬 때 꼭
start()를 사용하세요!
start()는 새로운 쓰레드에서run()을 실행하지만,
run()을 직접 호출하면 현재 쓰레드에서 실행됩니다.
// ✅ Thread 클래스 상속으로 쓰레드 구현
public class MyThread extends Thread {
@Override
public void run() {
String threadName = Thread.currentThread().getName();
System.out.println("::: " + threadName + "쓰레드 시작 :::");
for (int i = 0; i < 10; i++) {
System.out.println("현재 쓰레드: " + threadName + " - " + i);
try {
Thread.sleep(500); // 딜레이 0.5 초
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("::: " + threadName + "쓰레드 종료 :::");
}
}
public class Main {
public static void main(String[] args) {
System.out.println("::: main 쓰레드 시작");
MyThread thread0 = new MyThread();
MyThread thread1 = new MyThread();
// 1. thread0 실행
System.out.println("::: main 이 thread0 을 실행");
thread0.start();
// 2. thread1 실행
System.out.println("::: main 이 thread1 을 실행");
thread1.start();
System.out.println("::: main 쓰레드 종료");
}
}
::: main 쓰레드 시작 :::
::: main 이 thread0 을 실행
::: main 이 thread1 을 실행
::: main 쓰레드 종료 :::
::: Thread-1쓰레드 시작 :::
::: Thread-0쓰레드 시작 :::
현재 쓰레드: Thread-1 - 0
현재 쓰레드: Thread-0 - 0
현재 쓰레드: Thread-0 - 1
현재 쓰레드: Thread-1 - 1
현재 쓰레드: Thread-0 - 2
현재 쓰레드: Thread-1 - 2
현재 쓰레드: Thread-0 - 3
현재 쓰레드: Thread-1 - 3
현재 쓰레드: Thread-0 - 4
현재 쓰레드: Thread-1 - 4
현재 쓰레드: Thread-1 - 5
현재 쓰레드: Thread-0 - 5
현재 쓰레드: Thread-1 - 6
현재 쓰레드: Thread-0 - 6
현재 쓰레드: Thread-0 - 7
현재 쓰레드: Thread-1 - 7
현재 쓰레드: Thread-0 - 8
현재 쓰레드: Thread-1 - 8
현재 쓰레드: Thread-0 - 9
현재 쓰레드: Thread-1 - 9
::: Thread-0쓰레드 종료 :::
::: Thread-1쓰레드 종료 :::
join() 은 다른 쓰레드가 종료될 때까지 기다리게 하는 메서드입니다.join() 을 호출한 쓰레드가 끝날 때까지 main() 쓰레드가 대기합니다.main() 쓰레드가 너무 빨리 끝나지 않고 모든 작업이 완료된 후 종료되도록 할 때 유용합니다.main() 쓰레드가 총 작업 완료 시간을 측정할 수 있게 만들 수 있습니다.thread0, thread1) 가 실행되고 끝날 때까지 main() 이 기다립니다.main() 은 종료됩니다.public class Main {
public static void main(String[] args) {
System.out.println("::: main 쓰레드 시작");
MyThread thread0 = new MyThread();
MyThread thread1 = new MyThread();
// 시작시간 기록
long startTime = System.currentTimeMillis();
// 1. thread0 시작
System.out.println("thread0 시작");
thread0.start();
// 2. thread1 시작
System.out.println("thread1 시작");
thread1.start();
// ⌛️ main 쓰레드 대기 시키기
try {
thread0.join();
thread1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
long endTime = System.currentTimeMillis();
long totalTime = endTime - startTime;
System.out.println("총 작업 시간: " + totalTime + "ms");
System.out.println("::: main 쓰레드 종료");
}
}
::: main 쓰레드 시작 :::
thread0 시작
thread1 시작
::: Thread-1쓰레드 시작 :::
::: Thread-0쓰레드 시작 :::
현재 쓰레드: Thread-1 - 0
현재 쓰레드: Thread-0 - 0
현재 쓰레드: Thread-1 - 1
현재 쓰레드: Thread-0 - 1
현재 쓰레드: Thread-1 - 2
현재 쓰레드: Thread-0 - 2
현재 쓰레드: Thread-0 - 3
현재 쓰레드: Thread-1 - 3
현재 쓰레드: Thread-0 - 4
현재 쓰레드: Thread-1 - 4
현재 쓰레드: Thread-1 - 5
현재 쓰레드: Thread-0 - 5
현재 쓰레드: Thread-0 - 6
현재 쓰레드: Thread-1 - 6
현재 쓰레드: Thread-1 - 7
현재 쓰레드: Thread-0 - 7
현재 쓰레드: Thread-0 - 8
현재 쓰레드: Thread-1 - 8
현재 쓰레드: Thread-0 - 9
현재 쓰레드: Thread-1 - 9
::: Thread-1쓰레드 종료 :::
::: Thread-0쓰레드 종료 :::
총 작업 시간: 5054ms
// ✅ 쓰레드가 종료된 후 main()의 쓰레드가 종료됨
::: main 쓰레드 종료 :::
Thread 는 쓰레드를 제어하기 위해 존재하는 클래스입니다.Thread 클래스를 상속받아 MyThread를 구현하면 실행 로직 과 쓰레드 제어 로직 이 결합되어 한 가지 클래스에서 두 가지 역할을 담당하게 됩니다.start() , join() , isAlive() 등Runnable 을 활용하면 실행 로직을 별도의 구현체로 분리할 수 있습니다.Thread는 쓰레드를 제어하는 역할.Runnable 구현체 는 실행 로직을 관리.// ⚠️ Thread는 Thread 제어 역할, 실행 로직 두 가지를 담당합니다.
public class MyThread extends Thread {
@Override
public void run() {
String threadName = Thread.currentThread().getName();
System.out.println("현재 시작된 쓰레드: " + threadName);
for(int i = 0; i < 10; i++) {
System.out.println("현재 쓰레드 : " + threadName + " - " + i);
try {
Thread.sleep(500); // 딜레이 0.5 초
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("종료된 쓰레드: " + threadName);
}
}
다음과 같이 Runnable 인터페이스를 구현해서 코드를 분리합니다.
public class MyRunnable implements Runnable {
@Override
public void run() {
String threadName = Thread.currentThread().getName();
for (int i = 0; i < 10; i++) {
System.out.println("현재 쓰레드: " + threadName + " - " + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Main {
public static void main(String[] args) {
MyRunnable task = new MyRunnable(); // ✅ 하나의 작업 객체 선언
// ✅ 하나의 작업을 여러 쓰레드에서 공유
Thread thread0 = new Thread(task); // 작업 객체 공유
Thread thread1 = new Thread(task); // 작업 객체 공유
// 실행
thread0.start();
thread1.start();
}
}
Thread 를 상속해서 MyThread 를 구현하면 다른 클래스를 상속받지 못합니다.Thread 를 상속하면 다른 클래스를 상속할 수 없어서 확장성이 떨어집니다.Runnable 은 인터페이스이므로 기존 클래스의 기능을 유지하면서 상속을 통해 확장할 수 있습니다.public class MyNewClass { // ✅ 새로운 클래스
public void printMessage() {
System.out.println("MyClass 기능 실행");
}
}
public class MyThread extends Thread, MyNewClass{ // ❌ 다중 상속 불가
...
}
public class MyRunnable extends MyNewClass implements Runnable {
@Override
public void run() {
String threadName = Thread.currentThread().getName();
for (int i = 0; i < 10; i++) {
System.out.println("현재 쓰레드: " + threadName + " - " + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Main {
public static void main(String[] args) {
MyRunnable task = new MyRunnable();
// ✅ 기존 클래스를 유지하면서 확장해서 활용
task.printMessage();
Thread thread0 = new Thread(task);
Thread thread1 = new Thread(task);
thread0.start();
thread1.start();
}
}
챕터 3-7 : 쓰레드(Thread)