📙 목차
- 쓰레드(Thread)란?
- 싱글 쓰레드(Single Thread)
- 멀티 쓰레드(Multi-Thread)와 쓰레드 구현
- join() - 특정 쓰레드가 끝날 때까지 기다리기
- Runnable 인터페이스 활용 (권장)
- 실습 과제
🧡 학습 목표
- 쓰레드가 무엇인지 학습한다.
1. 쓰레드(Thread)란?
- 프로그램 내에서 독립적으로 실행되는 하나의 작업 단위
- 싱글 쓰레드 는 한 번에 하나의 작업만 처리하지만 멀티쓰레드는 여러 작업을 동시에 처리할 수 있다.
- 멀티 쓰레드를 활용하면 여러 작업을 병렬로 수행할 수 있어 처리 성능을 향상시킬 수 있다.
2. 싱글 쓰레드(Single Thread)
- 한 번에 하나의 작업만 처리
- 여러 개의 작업을 순차적으로 처리한다.
- 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("::: 작업 끝 :::");
}
}
3. 멀티 쓰레드(Multi-Thread)와 쓰레드 구현
멀티 쓰레드
- 여러 작업을 동시에 처리
- 여러 작업을 병렬로 동시에 처리할 수 있다.
쓰레드 구현
- Thread 클래스를 상속받아 쓰레드를 구현할 수 있다.
- Thread.run() 메서드를 오버라이드 해서 쓰레드가 수행할 작업을 정의할 수 있다.
- start() 메서드를 호출하면 새로운 쓰레드가 생성되고 run() 의 작업 내용이 실행된다.
- 쓰레드를 실행시킬 때 꼭 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 쓰레드 종료");
}
}
4. join() - 특정 쓰레드가 끝날 때까지 기다리기
- join() 은 main() 쓰레드가 다른 쓰레드가 종료될 때까지 기다리게 하는 메서드이다.
- join() 을 호출한 쓰레드가 끝날 때까지 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 쓰레드 종료");
}
}
5. Runnable 인터페이스 활용 (권장)
Runnalbe 인터페이스를 활용해 쓰레드를 구현
- 유지 보수성과 재사용성 향상
- 확장 가능성
유지 보수성과 재사용성 향상
- Thread 는 쓰레드를 제어하기 위해 존재하는 클래스이다.
- Thread 클래스를 상속받아 MyThread를 구현하면 실행 로직과 쓰레드 제어 로직이 결합되어 한 가지 클래스에서 두 가지 역할을 담당하게 된다.
- 하나의 클래스는 하나의 책임만 가지는 것이 유지 보수에 좋다.
- Runnable 을 활용하면 실행 로직을 별도의 구현체로 분리할 수 있다.
- Thread는 쓰레드를 제어하는 역할
- 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 를 구현하면 다른 클래스를 상속받지 못 한다.
- Java는 클래스의 다중 상속을 지원하지 않는다.
- Thread 를 상속하면 다른 클래스를 상속할 수 없어서 확장성이 떨어진다.
public class MyNewClass {
public void printMessage() {
System.out.println("MyClass 기능 실행");
}
}
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();
}
}
6. 실습 과제
싱글 쓰레드 VS 멀티 쓰레드 - 숫자 합계 시간을 비교해 보세요.
- 1 부터 1000 까지의 숫자 합계를 구합니다.
- 연산 사이에 10ms 딜레이를 추가하여 0.01초마다 계산 Thread.sleep(10);
- 싱글 쓰레드는 하나의 루프로 계산합니다.
- 멀티 쓰레드는 4개의 쓰레드가 각각 숫자를 나누어 계산합니다.
- 1 ~ 250
- 251 ~ 500
- 501 ~ 750
- 751 ~ 1000
- 두 방법의 실행 시간을 비교하여 멀티 쓰레드가 얼마나 더 빠른지 확인해 보세요.
package chapter3.thread.report;
public class MyRunnable implements Runnable {
private int start, end;
private int sum=0;
MyRunnable(int i, int j) {
this.start = i;
this.end = j;
}
public int getSum() {
return sum;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " 실행 시작: " + start + " ~ " + end);
for(int i = this.start; i <= this.end; i++) {
sum += i;
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + " 실행 완료. 합계: " + sum);
}
}
package chapter3.thread.report;
import java.util.logging.Level;
public class Main {
public static void main(String[] args) {
int a=1, b=1000;
long startTime, endtime;
// 싱글 스레드
startTime = System.currentTimeMillis();
MyRunnable single = new MyRunnable(a, b);
Thread singleThread = new Thread(single);
singleThread.start();
try {
singleThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
endtime = System.currentTimeMillis();
System.out.println("싱글 쓰레드 합계: " + single.getSum());
System.out.println("싱글 쓰레드 실행 시간: " + (endtime - startTime) + "ms\n");
// 멀티 스레드
int n=5;
startTime = System.currentTimeMillis();
MyRunnable[] multi = new MyRunnable[n];
Thread[] multiThread = new Thread[n];
for (int i = 0; i < n; i++) {
multi[i] = new MyRunnable(1 + i*b/n, (i+1)*b/n);
multiThread[i] = new Thread(multi[i]);
multiThread[i].start();
}
int sum = 0;
try {
for(int i = 0; i < multiThread.length; i++) {
multiThread[i].join();
sum += multi[i].getSum();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
endtime = System.currentTimeMillis();
System.out.println("멀티 쓰레드 합계: " + sum);
System.out.println("멀티 쓰레드 실행 시간: " + (endtime - startTime) + "ms");
}
}
👏 👏 👏 Java 문법 종합반 강의 완강 👏 👏 👏
튜터님이 설명을 쉽게 잘 해주셔서, 큰 어려움 없이 완주할 수 있었다.!
'Java > 강의' 카테고리의 다른 글
[📃 자바 입문] 자바란? (0) | 2025.04.30 |
---|---|
[📃 자바 입문] 프로그래머스 - 무료 강의 (0) | 2025.04.30 |
[📙 Java 문법 종합반] 3-6. 스트림(Stream) (1) | 2025.04.17 |
[📙 Java 문법 종합반] 3-5. 람다(Lambda) (0) | 2025.04.16 |
[📙 Java 문법 종합반] 3-4. 제네릭(Generic) (0) | 2025.04.16 |