Java 스레드 생성
자바에서 기본적으로 스레드를 만드는 방법으로 Runnable 인터페이스를 구현하는 방법과 Thread 클래스를 상속받는 방법이 있습니다. 그러면 자바 코드로 실제 스레드를 어떻게 생성하는지, Runnable 인터페이스와 Thread 클래스가 각각 어떤 역할을 하는지 살펴보겠습니다.
그럼 먼저 자바 코드로 스레드 생성 예제를 보겠습니다.
자바 스레드 생성 및 실행 코드
기본적으로 Runnable 인터페이스를 구현한 클래스를 만들어서 Thread 인스턴스에 인젝션해주는 방식입니다.
public static class RunnableImpl implements Runnable {
@Override
public void run() {
System.out.println("Hello World");
}
}
public static void main(String[] args) throws Exception {
Thread thread = new Thread(new RunnableImpl());
thread.start();
}
위와 같이 클래스를 새로 만들지 않고 바로 Runnable 인터페이스를 구현한 인스턴스를 인젝션 할 수도 있습니다.
public static void main(String[] args) throws Exception {
Thread thread = new Thread(new Runnable(){
@Override
public void run() {
System.out.println("Hello World");
}
});
thread.start();
}
또한 Java 8 버전 이상부터는 Runnable 인터페이스가 FunctionalInterface이기 때문에 다음과 같이 람다식을 적용하는 것도 가능합니다.
public static void main(String[] args) throws Exception {
Thread thread = new Thread(() ->
System.out.println("Hello World"));
thread.start();
}
마지막으로, Runnable 인터페이스를 구현하는 것이 아닌 Thread 클래스를 상속받아 스레드를 생성할 수도 있습니다.
public static class RunnableImpl extends Thread {
@Override
public void run() {
System.out.println("Hello World");
}
}
public static void main(String[] args) throws Exception {
Thread thread = new RunnableImpl();
thread.start();
}
Runnable 인터페이스와 Thread 클래스
그러면 Runnable 인터페이스는 어떤 역할을 하고 Thread 클래스는 어떤 역할을 하는 것일까요?
사실, 설명할 필요없이 객체 이름 그대로의 역할을 합니다.
Runnable 인터페이스는 run() 메소드 한 개를 구현하도록 되어있는데 이는 스레드가 시작될 때, 실행될 코드를 구현하는 것입니다. 즉, 이름 그대로 실행할 수 있는 코드를 구현하는 것입니다.
Thread 클래스는 자바에서 컴퓨팅 시스템의 스레드의 기능을 구현해준 클래스입니다. 컴퓨팅 시스템에서 스레드의 개념은 아주 중요하기 때문에 각 프로그래밍 언어마다 기본적으로 제공해주는데 자바에서는 Thread 클래스로 제공합니다.
그래서 Thread 인스턴스를 생성할 때, 실행될 코드를 Runnable 인터페이스의 run() 메소드를 오버라이딩하여 구현하고 Thread 클래스에 인젝션해주면 해당 코드가 실행되는 스레드가 생성되는 것입니다.
- Runnable 인터페이스: 스레드에서 실행될 코드를 run() 메소드에 구현
- Thread 클래스: 컴퓨팅 시스템의 스레드 기능을 가진 클래스
그리고 또 한 가지, Thread 클래스를 상속받아서 스레드를 생성하는 예제도 있었습니다. 사실, Thread 클래스도 Runnable 인터페이스를 구현한 클래스입니다. Thread 클래스에서 스레드가 시작했을 때 실행되는 코드는 Runnable 인터페이스의 run() 메소드를 오버라이딩한 Thread 클래스의 run() 메소드입니다. 그리고 이 Thread 클래스의 기본적인 run() 메소드의 구현은 스레드를 생성할 때 인젝션 받은 Runnable(target)의 run() 메소드를 호출하도록 하고 있습니다.
public
class Thread implements Runnable {
...
/* What will be run. */
private Runnable target;
...
@Override
public void run() {
if (target != null) {
target.run();
}
}
...
}
따라서, 스레드가 시작될 때, Thread 클래스의 run() 메소드가 호출되기 때문에 Thread 클래스를 상속받아 run() 메소드를 오버라이딩하여 실행 코드를 작성하면 해당 코드가 실행되는 것입니다.
이렇게 해서 실행될 코드를 Runnable 인터페이스를 구현하여 Thread 클래스에 인젝션하여 스레드를 생성하는 방법, Thread 클래스를 상속받아 run() 메소드를 오버라이딩하여 실행될 코드를 작성해 스레드를 생성하는 방법 모두 가능해지게 됩니다.
마지막으로, run() 메소드는 스레드가 시작될 때 실행되는 코드여서 사용자가 run() 메소드를 호출할 일은 없을 것입니다. 스레드를 생성하고 스레드를 시작하게되면 스레드 공간이 별도로 만들어지고 해당 공간에서 run() 메소드가 알아서 실행됩니다. 그래서 실행될 코드는 run() 메소드에 작성하고 스레드를 시작시킬 때는 start() 메소드를 호출합니다.
스레드 생성 방법에 관하여
스레드를 생성하는 방법을 2가지가 있었습니다.
실행될 코드를 Runnable 인터페이스를 구현하여 Thread 클래스에 인젝션하는 방법
Thread 클래스를 상속받아 run() 메소드를 오버라이딩하여 실행될 코드를 작성하는 방법
보통 1번 방식을 많이 사용할 것입니다. 2번 방식은 자바가 다중상속을 지원하지 않기 때문에 만약 Thread 클래스를 상속받으면 해당 클래스는 다른 클래스를 상속받을 수 없다는 문제가 있습니다. 그리고 1번 방식으로 클래스를 별도로 작성하지 않고 Thread 인스턴스를 생성할 때, 곧바로 Runnable 인터페이스를 구현하여 실행될 코드를 작성할 수 있고 람다 식으로도 작성하여 간결하게 스레드를 생성할 수 있다는 장점도 있겠습니다.
심플 예제 프로그램
예제 설명
3개의 스레드를 생성하고 각 스레드에 1-3번의 시퀀스를 붙여 thread n번의 이름을 붙인다. 각 스레드는 1-5를 순차적으로 출력하는데 자신의 시퀀스 번호와 출력 번호가 동일할 때, 1초 후에 출력한다.
소스코드
public static void main(String[] args) throws Exception {
List<Thread> threadPool = new ArrayList<>();
for (int i = 0; i < 3; i++) {
threadPool.add(new Thread(() -> {
String name = Thread.currentThread().getName();
int num = Integer.parseInt(name.split(" ")[1]);
int initSecond = LocalDateTime.now().getSecond();
for (int j = 0; j < 5; j++) {
if(num == (j+1)) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(
"[" + (LocalDateTime.now().getSecond() - initSecond) + "]" +
name + ": " +
num + "번 스레드의 " +
(j+1) + "번째 출력입니다.");
}
}, "thread " + (i+1)));
}
threadPool.forEach(t -> t.start());
}
출력 결과
출력 결과는 실행시킬 때마다 바뀔 수 있습니다.
[0]thread 3: 3번 스레드의 1번째 출력입니다.
[0]thread 2: 2번 스레드의 1번째 출력입니다.
[0]thread 3: 3번 스레드의 2번째 출력입니다.
[1]thread 1: 1번 스레드의 1번째 출력입니다.
[1]thread 2: 2번 스레드의 2번째 출력입니다.
[1]thread 2: 2번 스레드의 3번째 출력입니다.
[1]thread 3: 3번 스레드의 3번째 출력입니다.
[1]thread 3: 3번 스레드의 4번째 출력입니다.
[1]thread 2: 2번 스레드의 4번째 출력입니다.
[1]thread 1: 1번 스레드의 2번째 출력입니다.
[1]thread 2: 2번 스레드의 5번째 출력입니다.
[1]thread 3: 3번 스레드의 5번째 출력입니다.
[1]thread 1: 1번 스레드의 3번째 출력입니다.
[1]thread 1: 1번 스레드의 4번째 출력입니다.
[1]thread 1: 1번 스레드의 5번째 출력입니다.
'Java' 카테고리의 다른 글
[JAVA] 스레드 상태 제어 - 기본 (0) | 2021.06.16 |
---|
최근댓글