티스토리 뷰
1. Thread의 구현
Thread는 아래와 같이 두 방법으로 만들 수 있다.
- Thread 클래스를 상속받은 클래스를 만드는 방법
- Runnable 인터페이스를 구현한 클래스의 객체를 Thread 클래스의 생성자에 넘기는 방법
이때 run() 메서드를 반드시 오버라이딩하여 Thread가 수행할 작업을 정한다.
// 방법 1
class TestThread extends Thread {
@Override
public void run() {
int count = 10;
while(count-- > 0) {
System.out.println(toString() + ": " + count);
}
System.out.println(toString() + ": 카운트다운 끝");
}
}
// 방법 2
class TestRunnable implements Runnable {
@Override
public void run() {
int count = 10;
while(count-- > 0) {
System.out.println(toString() + ": " + count);
}
System.out.println(toString() + ": 카운트다운 끝");
}
}
public class ThreadPrac01 {
public static void main(String[] args) {
// 방법 1
TestThread t1 = new TestThread();
// 방법 2
TestRunnable r = new TestRunnable();
Thread t2 = new Thread(r);
t1.start();
t2.start();
}
}
# 실행결과:
Thread[Thread-0,5,main]: 9
sampleprj.TestRunnable@5dc1bd2: 9
Thread[Thread-0,5,main]: 8
sampleprj.TestRunnable@5dc1bd2: 8
sampleprj.TestRunnable@5dc1bd2: 7
Thread[Thread-0,5,main]: 7
Thread[Thread-0,5,main]: 6
Thread[Thread-0,5,main]: 5
Thread[Thread-0,5,main]: 4
sampleprj.TestRunnable@5dc1bd2: 6
sampleprj.TestRunnable@5dc1bd2: 5
sampleprj.TestRunnable@5dc1bd2: 4
Thread[Thread-0,5,main]: 3
Thread[Thread-0,5,main]: 2
Thread[Thread-0,5,main]: 1
sampleprj.TestRunnable@5dc1bd2: 3
sampleprj.TestRunnable@5dc1bd2: 2
Thread[Thread-0,5,main]: 0
Thread[Thread-0,5,main]: 카운트다운 끝
sampleprj.TestRunnable@5dc1bd2: 1
sampleprj.TestRunnable@5dc1bd2: 0
sampleprj.TestRunnable@5dc1bd2: 카운트다운 끝
일반적으로 Runnable 인터페이스를 구현하는 방법이 추천된다. Thread 클래스를 직접 상속받으면 그 외의 다른 클래스를 상속받을 수 없기 때문이다.
다만 Runnable 인터페이스를 구현하는 방법을 사용하는 경우 Thread 객체에 바로 접근할 수가 없다는 점을 유의해야 한다.
class TestThread2 extends Thread {
@Override
public void run() {
int count = 10;
while(count-- > 0) {
System.out.println(getName() + ": " + count);
}
System.out.println(getName() + ": 카운트다운 끝");
// 조상인 Thread의 메서드(getName)를 바로 호출했다
}
}
class TestRunnable2 implements Runnable {
@Override
public void run() {
int count = 10;
while(count-- > 0) {
System.out.println(Thread.currentThread().getName() + ": " + count);
// 현재 Thread를 반환해주는 Thread.currentThread()를 통해 Thread 객체에 접근했다.
}
System.out.println(Thread.currentThread().getName() + ": 카운트다운 끝");
}
}
public class ThreadPrac02 {
public static void main(String[] args) {
TestThread2 t1 = new TestThread2();
TestRunnable2 r = new TestRunnable2();
Thread t2 = new Thread(r);
t1.start();
t2.start();
}
}
# 실행결과:
Thread-0: 9
Thread-1: 9
Thread-0: 8
Thread-1: 8
Thread-0: 7
Thread-0: 6
Thread-1: 7
Thread-1: 6
Thread-1: 5
Thread-1: 4
Thread-0: 5
Thread-0: 4
Thread-0: 3
Thread-0: 2
Thread-1: 3
Thread-0: 1
Thread-0: 0
Thread-1: 2
Thread-0: 카운트다운 끝
Thread-1: 1
Thread-1: 0
Thread-1: 카운트다운 끝
2. Thread의 실행
start() 메서드를 통해 Thread 객체의 작업을 시작하게 할 수 있다. start()는 새로운 Thread를 생성하고 해당 Thread를 실행대기 상태로 만든다. OS에 의한 스케쥴러에 의해 실행대기 상태의 Thread는 큐와 같은 형태로 관리되며, 등록된 순서대로 run()이 호출되어 작업이 시작된다.
run()을 직접 호출하면 어떨까? 이렇게 되면 새로운 Thread를 만드는 것이 아니라, main()이 돌아가는 기존 Thread 위에서 run() 메서드가 작동할 뿐이다.
class SimpleRunnable implements Runnable {
@Override
public void run() {
try {
throw new Exception("고의로 예외를 발생시켰습니다.");
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class ThreadPrac02 {
public static void main(String[] args) {
Thread t1 = new Thread(new SimpleRunnable());
t1.start();
//t1.run();
}
}
# t1.start() 실행결과:
java.lang.Exception: 고의로 예외를 발생시켰습니다.
at sampleprj.SimpleRunnable.run(ThreadPrac02.java:7)
at java.base/java.lang.Thread.run(Thread.java:834)
# t1.run() 실행결과:
java.lang.Exception: 고의로 예외를 발생시켰습니다.
at sampleprj.SimpleRunnable.run(ThreadPrac02.java:7)
at java.base/java.lang.Thread.run(Thread.java:834)
at sampleprj.ThreadPrac02.main(ThreadPrac02.java:18)
실행결과를 보면, t1.run()을 호출한 경우 호출 스택에 main이 포함되어 있다. main Thread에서 예외가 발생한 것이다. 반면에 t1.start()를 호출한 경우 호출 스택에 main()이 포함되어 있지 않다. main Thread가 이미 종료된 상태에서 새로운 Thread가 돌아가던 중 예외가 발생한 것이다.
'컴퓨터 > 자바' 카테고리의 다른 글
멀티 쓰레드 환경에서 List를 사용해야할 때 (0) | 2024.02.18 |
---|