class MyThread extends Thread {
private String threadName;
public MyThread(String name) {
this.threadName = name;
}
public void run() {
for (int i = 1; i <= 5; i++) {
System.out.println(threadName + " - count: " + i);
try {
Thread.sleep(100); // 0.1초 대기
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
MyThread t1 = new MyThread("Thread A");
MyThread t2 = new MyThread("Thread B");
t1.start(); // 멀티스레드 시작
t2.start();
}
}
class MyRunnable implements Runnable {
private String threadName;
public MyRunnable(String name) {
this.threadName = name;
}
public void run() {
for (int i = 1; i <= 5; i++) {
System.out.println(threadName + " - count: " + i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Thread t1 = new Thread(new MyRunnable("Thread X"));
Thread t2 = new Thread(new MyRunnable("Thread Y"));
t1.start(); // 멀티스레드 시작
t2.start();
}
}
차이점
클래스를 Thread로부터 상속받음.
단일 상속만 가능해서, 이미 다른 클래스를 상속받고 있다면 사용이 불편.
스레드 객체 자체에 작업이 포함됨.
인터페이스 구현이므로, 여러 클래스 상속이 필요한 상황에서 유연하게 사용 가능.
작업(로직)과 스레드(실행) 객체를 분리할 수 있어 코드 구조가 더 깔끔함.
주로 이 방식을 더 선호함.
멀티스레딩 구성 핵심
각 스레드는 독립적으로 run() 메서드를 실행.
스레드 인스턴스마다 작업내용(메시지, 반복 횟수 등)을 개별적으로 가질 수 있음.
start()를 호출하면 각각의 스레드가 OS 혹은 JVM의 스케줄링에 따라 독립적으로 실행됨(동시성).
예제에서 두 개의 스레드가 번갈아가며 출력하는 모습이 멀티스레딩의 전형적인 동작.
주의사항
멀티스레드 환경에서는 데이터 경합(race condition), 동기화(synchronization) 문제에 주의해야 함.
필요하다면 synchronized 키워드나 Lock 등 동기화 도구 사용.