Java Thread 1부 - Thread는 실행 흐름이다
자바에서 Thread를 어렵게 생각할 필요는 없다.
Thread는 코드를 실행하는 흐름이다.
우리가 자바 프로그램을 실행하면 main() 메서드가 실행된다.
public class Main {
public static void main(String[] args) {
System.out.println("Hello Java");
}
}이 코드도 그냥 실행되는 것이 아니다.
main이라는 Thread 위에서 실행된다.
public class Main {
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName());
}
}실행 결과는 보통 다음과 같다.
main즉, 자바 프로그램은 기본적으로 main Thread에서 시작한다.
프로세스와 Thread
프로세스는 실행 중인 프로그램이다.
예를 들어 크롬을 실행하면 크롬 프로세스가 만들어지고, IntelliJ를 실행하면 IntelliJ 프로세스가 만들어진다.
Thread는 그 프로세스 안에서 실제로 코드를 실행하는 흐름이다.
쉽게 말하면 이렇다.
개념 | 비유 |
|---|---|
프로세스 | 회사 |
Thread | 회사에서 일하는 직원 |
회사가 있다고 일이 저절로 진행되지는 않는다.
직원이 있어야 일이 처리된다.
프로그램도 마찬가지다.
프로세스가 있어도 실제 코드를 실행하는 Thread가 필요하다.
왜 Thread를 사용할까?
이유는 단순하다.
여러 작업을 동시에 처리하기 위해서다.
예를 들어 프로그램에서 다음 일을 처리한다고 해보자.
파일 다운로드
사용자 입력 처리
화면 갱신
로그 저장
이 작업을 하나의 흐름에서만 처리하면, 파일 다운로드가 끝날 때까지 다른 작업이 멈출 수 있다.
하지만 Thread를 나누면 오래 걸리는 작업을 따로 처리할 수 있다.
main Thread → 화면 처리
worker Thread → 파일 다운로드이렇게 하면 프로그램이 멈추지 않고 자연스럽게 동작할 수 있다.
Thread 만들기 1 - Thread 상속
자바에서 Thread를 만드는 첫 번째 방법은 Thread 클래스를 상속하는 것이다.
class MyThread extends Thread {
@Override
public void run() {
System.out.println("실행 중: " + Thread.currentThread().getName());
}
}실행은 이렇게 한다.
public class Main {
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start();
System.out.println("main Thread 실행");
}
}핵심은 start()다.
start()를 호출해야 새로운 Thread가 만들어진다.
Thread 만들기 2 - Runnable 구현
두 번째 방법은 Runnable을 구현하는 것이다.
class MyTask implements Runnable {
@Override
public void run() {
System.out.println("실행 중: " + Thread.currentThread().getName());
}
}실행은 이렇게 한다.
public class Main {
public static void main(String[] args) {
Runnable task = new MyTask();
Thread thread = new Thread(task);
thread.start();
}
}실무에서는 보통 Runnable 방식이 더 자주 사용된다.
이유는 간단하다.
Thread는 실행 흐름이고,Runnable은 실행할 작업이다.
작업과 실행 흐름을 분리하면 코드가 더 깔끔해진다.
람다식으로 더 짧게 만들기
Runnable은 람다식으로 간단하게 만들 수 있다.
public class Main {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
System.out.println("새 Thread 실행");
});
thread.start();
}
}더 줄이면 이렇게도 가능하다.
new Thread(() -> {
System.out.println("작업 실행");
}).start();다만 실무에서는 Thread를 직접 계속 만들기보다, 보통 Thread Pool을 사용한다.
Thread Pool은 이후 글에서 다룬다.
start()와 run()은 완전히 다르다
Thread에서 가장 중요한 부분이다.
Thread thread = new Thread(() -> {
System.out.println(Thread.currentThread().getName());
});
thread.run();실행 결과는 보통 다음과 같다.
mainrun()을 직접 호출하면 새 Thread가 만들어지지 않는다.
그냥 일반 메서드를 호출한 것과 같다.
반면 start()를 호출하면 다르다.
Thread thread = new Thread(() -> {
System.out.println(Thread.currentThread().getName());
});
thread.start();실행 결과는 보통 다음과 같다.
Thread-0정리하면 이렇다.
호출 | 결과 |
|---|---|
| 현재 Thread에서 그냥 메서드 실행 |
| 새로운 Thread를 만들고 그 안에서 |
Thread를 새로 실행하고 싶다면 반드시 start()를 사용해야 한다.
실행 순서는 보장되지 않는다
다음 코드를 보자.
public class Main {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
System.out.println("worker Thread");
});
thread.start();
System.out.println("main Thread");
}
}결과는 이렇게 나올 수도 있고,
main Thread
worker Thread이렇게 나올 수도 있다.
worker Thread
main Thread왜냐하면 어떤 Thread를 먼저 실행할지는 운영체제가 결정하기 때문이다.
그래서 Thread 코드는 실행 순서를 함부로 믿으면 안 된다.
Thread는 한 번만 start() 할 수 있다
하나의 Thread 객체는 한 번만 실행할 수 있다.
Thread thread = new Thread(() -> {
System.out.println("Thread 실행");
});
thread.start();
thread.start();이 코드는 예외가 발생한다.
java.lang.IllegalThreadStateException같은 작업을 다시 실행하고 싶다면 새 Thread 객체를 만들어야 한다.
Runnable task = () -> {
System.out.println("Thread 실행");
};
new Thread(task).start();
new Thread(task).start();핵심 정리
Thread 1부의 핵심은 이것만 기억하면 된다.
Thread는 코드를 실행하는 흐름이다.
자바 프로그램은
main Thread에서 시작한다.Thread는 여러 작업을 동시에 처리하기 위해 사용한다.
Thread상속 방식보다Runnable방식이 더 유연하다.run()은 그냥 메서드 호출이다.start()를 호출해야 새 Thread가 만들어진다.Thread 실행 순서는 보장되지 않는다.
하나의 Thread 객체는 한 번만 실행할 수 있다.
Thread는 여기서부터 시작이다.
다음 글에서는 Thread의 상태와 제어 방법을 다룬다.
sleep(), join(), interrupt()가 왜 필요한지 알아보자.