Thread란?
개발이나 강의를 들으면서 'Thread' 라는 용어를 자주 들었다. 학부생 때 여러 전공 수업을 통해서 자주 접한 개념이었기에 전혀 낯선 용어라고 생각 들진 않았다. 하지만 실제로 프로젝트를 진행하면서 잘 써보지 않았기에 쓰레드(Thread)가 프로그램을 개발하면서 어떻게 쓰이는지 잘 알지 못했다. 그래서 쓰레드의 이론적인 개념을 잘 알고 있었지만 실제로 어떻게 적용하여 사용하는지 모르고 있었다. 오늘날 여러 프로그램이 효율적인 동작을 수행하기 위해서 쓰레드 개념을 활용하여 적용하고 있기 때문에 프로그램 설계에 있어서 쓰레드는 고려해야하는 요소이다. 이번 기회에 애매하게 알고 있던 쓰레드 개념을 확실하게 알아두어 프로젝트 진행 시에 필요할 때 적절하게 활용해려고 한다.
Thread 정의
쓰레드(Thread)의 정의는 '프로세스 내에 존재하는 프로그램을 실행하는 작업의 단위'로써, 프로세스의 메모리 등과 같은 자원을 공유하여 프로세스가 효율적으로 작업을 수행할 수 있도록 쓰레드가 역할을 대신하여 작업을 처리한다. 하나의 프로세스 내에 하나의 쓰레드가 존재하면 단일 쓰레드(Single Thread, 싱글 쓰레드)이고, 여러 개의 쓰레드가 존재하면 다중 쓰레드(Multi Thread, 멀티 쓰레드)이다. 그렇기 때문에 프로세스 내의 하나의 쓰레드가 전담하여 작업을 처리하는 것보다 여러 쓰레드가 작업을 나누어 분산 처리하는 것이 효과적으로 작업을 수행할 수 있기 때문에 프로세스가 작업을 수행하게 될 경우 다중 쓰레드를 적극 활용하는 것이 좋다. 소스 코드를 통해 직접 쓰레드를 실습하기 위한 목적으로 대표적으로 다중 쓰레드 프로그래밍을 지원하는 Java 언어를 활용하여 쓰레드가 무엇인지 알아본다.
Thread 클래스 상속과 쓰레드의 특징
public class ThreadPractice extends Thread {
@Override
public void run() {
super.run();
System.out.println("쓰레드 시작");
int sum = 0;
for (int i = 0; i < 10; i++) {
sum += i;
System.out.println(sum);
}
System.out.println("합계 : " + sum);
System.out.println("쓰레드 종료");
}
public static void main(String[] args) {
ThreadPractice threadPractice = new ThreadPractice();
System.out.println("메인 함수 시작");
threadPractice.start();
System.out.println("메인 함수 종료");
}
}
Thread 클래스를 상속받아 간단한 쓰레드 예제 코드를 작성해보았다. 쓰레드 동작을 정의하여 메인 함수에서 쓰레드를 동작시켜 어떻게 쓰레드가 동작하는지 확인해볼 수 있다. 아래 출력 결과를 보고 쓰레드가 어떤 결과를 출력할 지 알아보자.
쓰레드가 수행하는 동작을 출력 결과를 통해 살펴보면 의외의 사실을 파악할 수 있다. 메인 함수의 동작이 끝나더라도 쓰레드의 동작은 정의된 기능을 다 수행할 때까지 쓰레드가 종료되지 않을 수 있다는 점이다. 결국 외부의 제어에 종속되는 것이 아니라 독립적인 형태로 존재하여 정의된 기능을 다 수행하는 것이 쓰레드라는 것을 파악할 수 있다. 정확하게 이해하자면 사용자에 의해 별도로 정의된 쓰레드는 메인 쓰레드와 같은 흐름을 가지는 것이 아니라 독립적인 흐름을 가진다는 것을 알 수 있다. 이렇게 메인 쓰레드와는 다르게 별도 흐름을 가져가는 사용자 정의 쓰레드이기에 사용자가 의도한 결과가 나오기도 전에 메인 쓰레드가 끝나버릴 수도 있다. 이렇게 사용자 정의 쓰레드는 결국 '비동기적' 특성을 가지고 있어 프로그램의 동작을 수행하는 여러 쓰레드 중 어느 쓰레드가 먼저 끝나게 될 지 예측할 수 없다는 특징을 가지고 있다.
Runnable 인터페이스 상속
Thread 클래스를 상속받아서 쓰레드의 기능을 정의할 수 있는 것 뿐만 아니라, 인터페이스를 통해서도 쓰레드를 정의하여 사용할 수 있다. 바로 Runnable 인터페이스(Runnable Interface)를 활용하면 마찬가지로 쓰레드를 정의할 수 있는데 예시를 통해서 알아보자.
public class RunnablePractice implements Runnable {
@Override
public void run() {
System.out.println("Runnable 쓰레드 시작");
int sum = 0;
for (int i = 0; i < 10; i++) {
sum += i;
System.out.println(sum);
}
System.out.println("합계 : " + sum);
System.out.println("Runnable 쓰레드 종료");
}
public static void main(String[] args) {
RunnablePractice runnablePractice = new RunnablePractice();
Thread thread = new Thread(runnablePractice);
System.out.println("메인 함수 시작");
thread.start();
System.out.println("메인 함수 종료");
}
}
Thread 클래스를 상속받아서 쓰레드를 정의하고 사용하는 것과 다르게 Runnable 인터페이스를 활용하면 바로 객체를 생성하고 쓰레드를 실행시킬 순 없다. 별도로 쓰레드 객체를 생성하여 파라미터로 Runnable를 활용한 객체를 넣어준다음, 쓰레드 객체를 통해 start 함수를 호출시켜주어야 쓰레드가 정상적으로 실행된다. 별도 쓰레드 객체 없이 run 함수를 호출하면 일반적으로 단순히 해당 객체의 함수를 호출하는 형태이기에 애초에 쓰레드가 생성되지 않는다. 그렇기에 Runnable 인터페이스를 통해 정의한 쓰레드를 실행시키려면 별도 Thread 객체를 생성하여 파라미터로 넘겨주는 형태로 쓰레드 객체로 감싸주어 해당 쓰레드를 실행시키도록 해야한다.
데몬 쓰레드(Daemon Thread)
일반적으로 쓰레드가 비동기적 특성을 가지고 있는 반면에, 메인 쓰레드의 동작을 보조해주면서 메인 쓰레드가 끝나면 같이 동작이 끝나버리는 쓰레드가 있다. 이 쓰레드를 바로 '데몬 쓰레드(Daemon Thread)'라고 한다. 메인 쓰레드의 동작이 끝나면 보조 쓰레드가 기능이 아직 끝나지 않더라도 더 이상 보조 쓰레드의 기능 수행이 의미가 없게 되는 경우가 있다. 이러한 경우에 쓰일 수 있는 것이 바로 데몬 쓰레드이다. 예시를 통해서 데몬 쓰레드의 동작을 살펴보자.
public class DaemonThread extends Thread {
@Override
public void run() {
super.run();
while ( true ) {
System.out.println("Running Daemon Thread!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Daemon Thread End!");
break;
}
}
public static void main(String[] args) {
DaemonThread daemonThread = new DaemonThread();
daemonThread.setDaemon(true);
System.out.println("메인 함수 시작");
daemonThread.start();
System.out.println("메인 함수 끝");
}
}
데몬 쓰레드를 sleep 함수를 통해서 잠깐 멈추게 한 후 메인 쓰레드가 종료되면 같이 종료되는지 확인하는 목적으로 예시 코드를 작성했다. 데몬 쓰레드가 아닌 일반적인 쓰레드일 경우 메인 쓰레드가 먼저 끝나게 되더라도, 쓰레드에 주어진 기능을 다 수행할 때까지 종료되지 않는게 원칙이다. 앞서 이야기한 데몬 쓰레드 같은 경우 메인 쓰레드가 끝나면 강제로 종료되는 특성을 가지고 있어야 하는데, 출력 결과를 통해서 과연 그런 특성을 가지고 있는지 확인해보자.
데몬 쓰레드가 생성되고 실행되자마자 메인 쓰레드가 끝나버려 데몬 쓰레드의 끝인 'Daemon Thread End!'라는 출력이 나타나지 않는 것을 확인할 수 있다. 이러한 출력 결과를 통해 메인 쓰레드가 끝나면 강제로 데몬 쓰레드도 종료된다는 사실을 확인할 수 있다. 원래 쓰레드는 비동기적 특성을 가지고 있지만, 'setDaemon()'이라는 데몬 설정을 통해 메인 쓰레드의 실행이 끝나면 기능을 수행하는 도중이라도 강제적으로 종료될 수 있는 데몬 쓰레드로 정의할 수 있다. 이렇게 하면 별도로 정의한 쓰레드일지라도 메인 쓰레드가 끝나면 같이 끝나도록 의도할 수 있어 사용자가 의도한 결과를 얻을 수 있게 된다.
이렇게 간단한 예시를 통해서 쓰레드의 특성과 사용법을 알아보았다. 쓰레드를 정의하는 방법이 Thread 클래스 상속 말고도 Runnable 인터페이스를 통해 가능하다는 것을 알게 되었고, Runnable 인터페이스가 더 자주 이용된다는 사실을 Thread에 대해 여러 자료를 찾아보면서 알게 되었다. 또한 데몬 쓰레드를 통해서 메인 쓰레드의 종료와 함께 강제적으로 종료될 수 있다는 것도 새롭게 알게 되어 흥미로웠다. Java에서 쓰레드는 여러 옵션이 존재하여 쓰레드의 우선순위를 정한다든가 join 함수를 통해서 쓰레드의 분기를 합친다든가 등 쓰레드의 기능을 유용하게 활용할 수 있다. 하지만 다 알아보고 직접 실습하며 이번 글에서 언급하기엔 양이 너무 많기에 어떻게 쓰이는지 개념정도만 파악하는 정도로 이번 글을 마치려고 한다.
- 참고
https://beststar-1.tistory.com/6
스레드(Thread) - 개념, 사용이유, 프로세스와의 비교, 상태, 우선순위, 종류
프로세스 운영체제에 의해 메모리 공간을 할당받아 CPU에서 실행/제어되고 있는 프로그램이다. 종종 '스케줄링의 대상이 되는 작업'이라는 용어와 거의 같이 쓰인다. 데이터(data) + 자원(memory) +
beststar-1.tistory.com
https://goodgid.github.io/What-is-Thread/
쓰레드(Thread)란 무엇인가?
Index
goodgid.github.io
https://coding-factory.tistory.com/279
[Java] 자바 Thread(스레드) 사용법 & 예제
Thread란? 하나의 프로세스 내부에서 독립적으로 실행되는 하나의 작업 단위를 말하며, 세부적으로는 운영체제에 의해 관리되는 하나의 작업 혹은 태스크를 의미합니다. 스레드와 태스크(혹은
coding-factory.tistory.com
https://reakwon.tistory.com/84
[JAVA/자바] 쓰레드(Thread) 다루기( Thread상속, Runnable 구현, join)
쓰레드(Thread) 쓰레드(Thread)는 간단히 정의하면 하나의 프로세스(실행중인 프로그램이라고 합니다.)에서 독립접으로 실행되는 하나의 일, 또는 작업의 단위를 말합니다. 뭐, 더 간단히 말해 쓰레
reakwon.tistory.com
https://honbabzone.com/java/java-thread/
JAVA 쓰레드란(Thread) ? - JAVA에서 멀티쓰레드 사용하기
JAVA에서 Thread사용하는 방법을 배우고 멀티코어 환경에서 멀티 쓰레드를 사용하는 방법을 알아보겠습니다.
honbabzone.com
'기본기 다지기 > 문법' 카테고리의 다른 글
enum & struct (0) | 2022.06.12 |
---|---|
Thread 실습 - Java (0) | 2022.04.16 |
추상클래스(abstract class)와 인터페이스(interface) (0) | 2022.03.27 |
static 이란? (0) | 2022.03.13 |