티스토리 뷰
주의 사항!
- 이 글은 제가 직접 공부하는 중에 작성되고 있습니다.
- 따라서 제가 이해하는 그대로의 내용이 포함됩니다.
- 따라서 이 글은 사실과는 다른 내용이 포함될 수 있습니다.
멀티 스레드로 실행하는 애플리케이션을 개발하려면 먼저 몇 개의 작업을 병렬로 실행할지 결정하고 각 작업별로 스레드를 생성해야 합니다. 어떤 자바 애플리케이션이건 메인 스레드는 반드시 존재하기 때문에 메인 작업 이외에 추가적인 병렬 작업의 수만큼 스레드를 생성하면 됩니다.
자바에서는 작업 스레드도 객체로 생성되기 때문에 클래스가 필요합니다. java.lang.Thead 클래스를 직접 객체화해서 생성해도 되지만, Thread를 상속해서 하위 클래스를 만들어 생성할 수도 있습니다.
Thread 클래스로부터 직접 생성
java.lang.Thread 클래스로부터 작업 스레드 객체를 직접 생성하려면 다음과 같이 Runnable을 매개 값으로 갖는 생성자를 호출해야 합니다.
Thread thread = new Thread(Runnable target);
Runnable은 작업 스레드가 실행할 수 있는 코드를 가지고 있는 객체라고 해서 붙여진 이름입니다. Runnable은 인터페이스 타입이기 때문에 구현 객체를 만들어 대입해야 합니다.
Runnable에는 run() 메서드 하나가 정의되어 있습니다. Runnable 구현 클래스는 run()을 오버 라이딩 해성 작업 스레드가 실행할 코드를 작성해야 합니다. 다음은 Runnable 구현 클래스를 작성하는 방법을 보여줍니다.
class Task implements Runnable {
public void run() {
//스레드가 실행할 코드;
}
}
Runnable은 작업 내용을 가지고 있는 객체이지 실제 스레드는 아닙니다. Runnable 구현 객체를 생성한 후, 이것을 매개 값으로 해서 Thread 생성자를 호출하면 비로소 작업 스레드가 생성됩니다.
Runnable task = new Task(); //작업 스레드에서 실행할 코드를 가지고 있는 객체 생성
Thread thread = new Thread(task); //task 객체를 매개 값으로 주고 작업 스레드 생성
코드를 좀 더 절약하기 위해 Thread 생성자를 호출할 때 Runnable 익명 객체를 매개 값으로 사용할 수도 있습니다. 오히려 이 방법이 더 많이 사용됩니다.
Thread thread = new Thread(new Bunnale() {
//스레드가 실행할 코드;
});
Runnable 인터페이스는 run() 메서드 하나만 정의되어 있기 때문에 함수적 인터페이스입니다. 따라서 다음과 같이 람다식을 매개 값으로 사용할 수도 있습니다. 가장 간단한 방법이지만, 자바 8부터 지원되기 때문에 자바 7 이전 버전에서는 사용할 수 없습니다. 람다식은 나중에 배우게 됩니다.
Thread thread = new Thread( () -> {
//스레드가 실행할 코드;
});
작업 스레드는 생성되는 즉시 실행되는 것이 아닙니다. start() 메서드를 다음과 같이 호출해야만 비로소 실행됩니다.
thread.start();
start() 메서드가 호출되면, 작업 스레드는 매개 값으로 받은 Runnable의 run() 메서드를 실행하면서 자신의 작업을 처리합니다.
0.5초 주기로 비프(beep) 음을 발생시키면서 동시에 프린팅 하는 작업이 있다고 가정해보겠습니다. 비프음 발생과 프린팅은 서로 다른 작업이므로 메인 스레드가 동시에 두 가지 작업을 처리할 수는 없습니다. 만약 다음과 같이 작성했다면 메인 스레드는 비프음을 모두 발생한 다음, 프린팅을 시작합니다.
//Main.java
package Example;
import java.awt.Toolkit;
public class Main {
public static void main(String[] args) {
Toolkit toolkit = Toolkit.getDefaultToolkit();
for(int i = 0; i < 5; i++) {
toolkit.beep();
try {
Thread.sleep(500);
} catch (Exception e) {}
}
for(int i = 0; i < 5; i++) {
System.out.println("띵");
try {
Thread.sleep(500);
} catch (Exception e) {}
}
}
}
/*
실행결과
(비프음이 0.5초 간격으로 5번 울린 후)
띵
띵
띵
띵
띵
*/
비프음을 발생시키면서 동시에 프린팅을 하려면 두 작업 중 하나를 메인 스레드가 아닌 다른 스레드에서 실행시켜야 합니다. 프린팅은 메인 스레드가 담당하고 비프음을 들려주는 것을 작업 스레드가 담당하도록 수정해보겠습니다.
//Main.java
package Example;
import java.awt.Toolkit;
public class Main {
public static void main(String[] args) {
Thread thread = new Thread( new Runnable() {
@Override
public void run() {
Toolkit toolkit = Toolkit.getDefaultToolkit();
for(int i = 0; i < 5; i++) {
toolkit.beep();
try {
Thread.sleep(500);
} catch(Exception e) {}
}
}
});
thread.start();
for(int i = 0; i < 5; i++) {
System.out.println("띵");
try {
Thread.sleep(500);
} catch(Exception e) {}
}
}
}
/*
실행결과
(비프음과 동시에 "띵" 출력)
띵
띵
띵
띵
띵
*/
Thread 생성자를 호출할 때 Runnable 임시 객체를 생성하는 모습입니다.
Thread 하위 클래스로부터 생성
작업 스레드가 실행할 작업을 Runnable로 만들지 않고, Thread 하위 클래스로 작업 스레드를 정의하면서 작업 내용을 포함시킬 수도 있습니다. 다음은 작업 스레드 클래스를 정의하는 방법인데, Thread 클래스를 상속한 후 run 메서드를 오버 라이딩해서 스레드가 실행할 코드를 작성하면 됩니다. 작업 스레드 클래스로부터 작업 스레드 객체를 생성하는 방법은 일반적인 객체를 생성하는 방법과 동일합니다.
public class WorkerThread extends Thread {
@Override
public void run() {
//스레드가 실행할 코드;
}
}
코드를 좀 더 절약하기 위해 다음과 같이 Thread 익명 객체로 작업 스레드 객체를 생성할 수도 있습니다.
Thread thread = new Thread() {
@Override
public void run() {
//스레드가 실행할 코드;
}
};
이렇게 생성된 작업 스레드 객체에서 start() 메서드를 호출하면 작업 스레드는 자신의 run() 메서드를 실행하게 됩니다.
이전에 비프음과 출력을 동시에 하는 예제를 수정하여 Thread 하위 클래스로부터 생성하는 예제를 보이겠습니다. 임시 객체를 생성하는 방법을 사용했습니다.
//Main.java
package Example;
import java.awt.Toolkit;
public class Main {
public static void main(String[] args) {
Thread thread = new Thread() {
@Override
public void run() {
Toolkit toolkit = Toolkit.getDefaultToolkit();
for(int i = 0; i < 5; i++) {
toolkit.beep();
try {
Thread.sleep(500);
} catch(Exception e) {}
}
}
};
thread.start();
for(int i = 0; i < 5; i++) {
System.out.println("띵");
try {
Thread.sleep(500);
} catch(Exception e) {}
}
}
}
/*
실행결과
(비프음과 동시에 출력)
띵
띵
띵
띵
띵
*/
스레드의 이름
스레드는 자신의 이름을 가지고 있습니다. 스레드의 이름이 큰 역할을 한느 것은 아니지만, 디버깅할 때 어떤 스레드가 어떤 작업을 하는지 조사할 목적으로 가끔 사용됩니다. 메인 스레드는 "main"이라는 이름을 가지고 있고, 우리가 직접 생성한 스레드는 자동적으로 "Thread-n"이라는 이름으로 설정됩니다. n은 스레드의 번호를 말합니다. Thread-n 대신 다른 이름으로 설정하고 싶다면 Thread 클래스의 setName() 메서드로 변경하면 됩니다.
thread.setName("스레드 이름");
반대로 스레드 이름을 알고 싶을 경우에는 getName() 메서드를 호출하면 됩니다.
thread.getName();
setName()과 getName()은 Thread의 인스턴스 메서드이므로 스레드 객체의 참조가 필요합니다. 만약 스레드 객체의 참조를 가지고 있지 않다면, Thread의 정적 메서드인 currentThread()로 코드를 실행하는 현재 스레드의 참조를 얻을 수 있습니다.
Thread thread = Thread.currentThread();
다음 예제는 메인 스레드의 참조를 얻어 스레드 이름을 콘솔에 출력하고, 새로 생성한 스레드의 이름을 setName() 메서드로 설정한 후, getName() 메서드로 읽어오도록 했습니다.
//Main.java
package Example;
public class Main {
public static void main(String[] args) {
Thread mainThread = Thread.currentThread();
System.out.println("프로그램 시작 스레드 이름 : " + mainThread.getName());
Thread threadA = new Thread() {
@Override
public void run() {
setName("ThreadA");
System.out.println(getName() + "가 출력한 내용");
}
};
System.out.println("작업 스레드 이름 : " + threadA.getName());
threadA.start();
Thread threadB = new Thread() {
@Override
public void run() {
setName("threadB");
System.out.println(getName() + "가 출력한 내용");
}
};
System.out.println("작업 스레드 이름 : " + threadB.getName());
threadB.start();
}
}
/*
실행결과
프로그램 시작 스레드 이름 : main
작업 스레드 이름 : Thread-0
작업 스레드 이름 : Thread-1
ThreadA가 출력한 내용
threadB가 출력한 내용
*/
'공부 일지 > JAVA 공부 일지' 카테고리의 다른 글
자바, 동기화 메서드와 동기화 블록 (0) | 2021.04.26 |
---|---|
자바, 스레드 우선 순위 (0) | 2021.04.26 |
자바, 멀티 스레드 개념 (0) | 2021.04.22 |
자바, java.time 패키지 (0) | 2021.04.22 |
자바, Format 클래스 (0) | 2021.04.21 |