Java에서 Thread를 생성하고, 실행, 정지하는 방법에 대해서 알아보겠습니다.
Thread 생성 및 실행
다음과 같이 Thread를 생성하고 실행할 수 있습니다.
Thread thread = new Thread();
thread.start();
위의 코드는 단순히 쓰레드만 생성합니다.
만약 어떤 코드가 동작하도록 만드려면 Thread를 상속받아 구현을 해야 합니다.
다음과 같이 Thread를 상속받고 run()
을 오버라이딩하면 Thread가 실행될 때 run()
이 호출됩니다.
public class MyThread extends Thread {
public MyThread() {
}
@Override
public void run() {
log("Thread is running");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
log("Finish run()");
}
}
다음과 같이 위에서 구현한 Thread를 실행할 수 있습니다.
MyThread thread = new MyThread();
log("Start thread");
thread.start();
Output:
19:25:13.418 (main) Start thread
19:25:13.419 (Thread-0) Thread is running
19:25:16.419 (Thread-0) Finish run()
참고로 log()
는 아래와 같이 시간과 쓰레드 이름을 함께 출력해주는 로그 메소드입니다.
public static void log(String msg) {
System.out.println(LocalTime.now() + " ("
+ Thread.currentThread().getName() + ") " + msg);
}
Thread, Runnable
Thread를 상속받아 구현하지 않고 Runnable
을 구현한 객체를 Thread에 전달하여 실행하는 방법도 있습니다.
다음과 같이 Runnable을 구현할 수 있습니다.
public static class MyThreadRunnable implements Runnable {
private final AtomicBoolean running = new AtomicBoolean(false);
public MyThreadRunnable() {
}
@Override
public void run() {
log("Thread is running");
log("Do something");
}
}
그리고 아래와 같이 Thread의 인자로 Runnable을 전달해주면 thread가 실행될 때 Runnable에 구현된 run()
이 실행됩니다.
MyThreadRunnable runnable = new MyThreadRunnable();
Thread thread = new Thread(runnable);
log("Start thread");
thread.start();
Output:
19:28:57.276 (main) Start thread
19:28:57.277 (Thread-0) Thread is running
19:28:57.277 (Thread-0) Do something
Join
main 쓰레드에서 다른 쓰레드의 작업이 모두 끝날 때 까지 기다릴 때도 있습니다. 이럴 때 join()
을 사용할 수 있습니다.
아래 예제에서 join()
을 호출하면, thread의 작업이 모두 완료되기 전까지 blocking되어 대기 상태가 됩니다. 작업이 완료되면 join()
은 리턴되고 다음 코드를 실행하게 됩니다.
MyThread thread = new MyThread();
log("Start thread");
thread.start();
thread.join();
log("Everything is done");
Join, timeout 설정
Thread가 종료되지 않는 경우 join()
도 무한히 대기할 수 있습니다.
Timeout을 설정하여 일정 시간이 지나면 더 이상 기다리지 않도록 만들 수 있습니다.
Timeout은 join(miliseconds)
처럼 인자로 시간을 전달하여 설정할 수 있습니다.
MyThread thread = new MyThread();
log("Start thread");
thread.start();
thread.join(1000);
log("Everything is done");
결과를 보면 1초 내에 쓰레드가 완료되지 않으면 기다리지 않고 다음 코드를 수행하는 것을 알 수 있습니다.
22:08:41.365 (main) Start thread
22:08:41.366 (Thread-0) Thread is running
22:08:42.366 (main) Everything is done
22:08:44.366 (Thread-0) Finish run()
Thread 종료
Thread.stop()
은 쓰레드를 안전하게 종료하지 못하여 더 이상 지원하지 않고 있습니다.(deprecated)
대신 flag 방식으로 쓰레드를 종료하도록 구현하거나, Interrupt 방식으로 쓰레드를 종료하도록 구현해야 합니다.
Flag 방식으로 쓰레드 종료
다음과 같이 Runnable을 구현하였습니다. 이 클래스에는 running
이라는 객체가 있습니다. 이것을 on, off하여 쓰레드를 실행하거나 종료하는 의미로 사용할 것입니다.
public static class MyThreadRunnable implements Runnable {
private final AtomicBoolean running = new AtomicBoolean(false);
public MyThreadRunnable() {
}
public void stop() {
log("Stop this thread");
running.set(false);
}
public void run() {
running.set(true);
while (running.get()) {
try {
log("Thread is running");
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.out.println("Thread was interrupted");
}
}
}
}
다음과 같이 Thread를 생성하고 실행하였습니다. 5초 뒤에 runnable.stop()
을 호출하여 running
을 false로 변경하여 run()
이 끝나도록 만들었습니다.
MyThreadRunnable runnable = new MyThreadRunnable();
Thread thread = new Thread(runnable);
thread.start();
Thread.sleep(5000);
log("Send stop signal");
runnable.stop();
Output:
19:39:13.038 (Thread-0) Thread is running
19:39:14.039 (Thread-0) Thread is running
19:39:15.039 (Thread-0) Thread is running
19:39:16.039 (Thread-0) Thread is running
19:39:17.039 (Thread-0) Thread is running
19:39:17.946 (main) Send stop signal
19:39:17.946 (main) Stop this thread
run()
이 모두 끝나면 쓰레드도 종료되기 때문에 이런식으로 구현할 수 있습니다.
Interrupt 방식으로 쓰레드 종료
이번에는 다음과 같이 Runnable을 구현하였습니다. runnable 플래그도 없고 실행되면 무한히 동작하게 됩니다.
public static class MyThreadRunnable implements Runnable {
public MyThreadRunnable() {
}
public void run() {
while (true) {
try {
log("Thread is running");
Thread.sleep(1000);
} catch (InterruptedException e) {
log("Thread was interrupted.");
log("Release some resources");
log("Done");
return;
}
}
}
}
다음과 같이 쓰레드를 실행하였습니다. 그리고 5초 뒤에 interrupt()
를 호출하였습니다.
Thread thread = new Thread(new MyThreadRunnable());
thread.start();
Thread.sleep(5000);
log("isInterrupted(): " + thread.isInterrupted());
log("Interrupt this thread");
thread.interrupt();
log("isInterrupted(): " + thread.isInterrupted());
interrupt()
가 호출되면 쓰레드의 동작하는 코드에서 InterruptedException가 발생합니다.
이 예외가 발생했을 때 자원을 정리하고 종료되도록 만들면 됩니다.
- interrupt(): Interrupt 예외를 발생시킵니다.
- isInterrupted(): Intrrupted 상태인지 결과를 boolean으로 리턴합니다.
Output:
19:46:10.010 (Thread-0) Thread is running
19:46:11.010 (Thread-0) Thread is running
19:46:12.011 (Thread-0) Thread is running
19:46:13.011 (Thread-0) Thread is running
19:46:14.011 (Thread-0) Thread is running
19:46:14.946 (main) isInterrupted(): false
19:46:14.946 (main) Interrupt this thread
19:46:14.946 (main) isInterrupted(): true
19:46:14.946 (Thread-0) Thread was interrupted.
19:46:14.946 (Thread-0) Release some resources
19:46:14.946 (Thread-0) Done
Related Posts
- Java - 람다식(Lambda)과 함수형 인터페이스
- Java - 직렬화(Serialize), 역직렬화(Deserialize)
- Java - do while과 while의 차이점
- Java - List empty(null) 체크, 3가지 방법
- Java - ArrayList 초기화, 4가지 방법
- Java - HashSet.addAll()
- Java - HashSet.contains()
- Java - 2차원 배열 선언, 출력 방법
- Java - 리스트 정렬, 3가지 방법
- Java - HashSet 정렬, 3가지 방법
- Java - 리스트 (List, ArrayList)
- Java - 첫 글자만 대문자로 바꾸는 방법
- Java - hashCode(), 사용하는 이유? 구현 방법?
- Java8의 Stream reduce() 사용 방법 및 예제
- Java - filter, map, flatMap 사용 방법 및 예제
- Java - 2개의 Map 합치기 (merge, putAll)
- Java - ConcurrentModificationException 원인 및 해결 방법
- JUnit - @After와 @AfterClass의 차이점
- JUnit - @Before와 @BeforeClass의 차이점
- java와 javac의 차이점
- Java - 자바(JDK) 버전 확인 방법 (터미널, cmd 명령어)
- Java - java.util.Date를 java.sql.Date로 변환
- Java - 시스템 운영체제(OS) 정보 확인
- Java - 코드 실행 시간 측정
- Java - HashSet.retainAll() 사용 방법 및 예제
- Java - ArrayList.retainAll() 사용 방법 및 예제
- Java - ArrayList를 String으로 변환
- Java - float을 int로 변환
- Java - float을 String으로 변환
- Java - String을 boolean으로 변환
- Java - XML을 JSON으로 변환
- Java - ClassNotFoundException 발생 원인 및 해결 방법
- Java - private 생성자를 사용하는 이유
- Java - non-static method cannot be referenced from a static context
- Java - NoSuchMethodError 원인 및 해결 방법