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 - Unsupported class file major version 61 에러
- Java - String.matches()로 문자열 패턴 확인 및 다양한 예제 소개
- Java - 문자열 공백제거 (trim, replace)
- Java - replace()와 replaceAll()의 차이점
- Java - ArrayList 초기화, 4가지 방법
- Java - 배열 정렬(Sorting) (오름차순, 내림차순)
- Java - 문자열(String)을 비교하는 방법 (==, equals, compare)
- Java - StringBuilder 사용 방법, 예제
- Java - 로그 출력, 파일 저장 방법 (Logger 라이브러리)
- Java IllegalArgumentException 의미, 발생 이유
- Java - NullPointerException 원인, 해결 방법
- Seleninum의 ConnectionFailedException: Unable to establish websocket connection 해결
- Java - compareTo(), 객체 크기 비교
- Java - BufferedWriter로 파일 쓰기
- Java - BufferedReader로 파일 읽기
- Java charAt() 함수 알아보기
- Java - BigInteger 범위, 비교, 연산, 형변환
- Java contains()로 문자(대소문자 X) 포함 확인
- Java - Set(HashSet)를 배열로 변환
- Java - 문자열 첫번째 문자, 마지막 문자 확인
- Java - 문자열 한글자씩 자르기
- Java - 문자열 단어 개수 가져오기
- Java - 1초마다 반복 실행
- Java - 배열을 Set(HashSet)로 변환
- Java - 여러 Set(HashSet) 합치기
- Java - 명령행 인자 입력 받기
- Java - 리스트 역순으로 순회, 3가지 방법
- Java - 특정 조건으로 리스트 필터링, 3가지 방법
- Java - HashMap 모든 요소들의 합계, 평균 계산
- Java - 특정 조건으로 HashMap 필터링
- Java - 싱글톤(Singleton) 패턴 구현
- Java - 숫자 왼쪽에 0으로 채우기
- Java - String 배열 초기화 방법
- Java - 정렬된 순서로 Map(HashMap) 순회
- Java - HashMap에서 key, value 가져오기