안녕하세요
저번에 ThreadLocal을 하면서 우선순위로 Thread 설명 정리를 먼저 했어야 했는데
뒷순위로 정리를 하게 되었는데요 ㅎㅎ

| Thread란 간단하게 여러 개의 작업을 동시에 할 수 있는 것입니다 |
밑에서 더 자세하게 설명해드릴게요
●멀티 태스킹?

멀티 태스킹이란 멀티(multi)+태스킹(tasking)의 합성어로서 다수의 작업을 동시에 처리하는 것을 말합니다. 위에 그림처럼 우리는 다양한 작업을 멀티태스킹을 이루어지고 있습니다.
예를 들어서 유튜브에 노래를 틀어놓고 공부하는 작업을 한다던가, 핸드폰 노래를 들으면서 인스타그램을 본다던가 다양하게 멀티태스킹을 합니다. 본래 멀티태스킹은 컴퓨터 기술 용어로써 여러 프로그램 코드(작업, 테스크)가 동시에 실행되는 것을 말합니다.
●스레드(Thread)
스레드(thread)란 운영 체제에의해 관리되는 하나의 작업 혹은 태스크를 말하며, 다수의 스레드를 동시에 실행시키도록 응용프로그램을 작성하는 기법을 멀티스레딩(muti-threading)이라고 부른다.태스크와 스레드는 서로 바꾸어 사용해도 무관합니다. 프로세스(process) 내에서 실제로 작업을 수행하는 주체를 의미합니다. 모든 프로세스에는 한 개 이상의 스레드가 존재하여 작업을 수행합니다. 두 가지 이상의 스레드를 가지는 것을 위에 처럼 멀티스레드 프레세스(multi-threaded process)라고 합니다.
public class Sample extends Thread{
@Override
public void run() { //Thread를 상송하면 run 메서드를 구현해야 합니다.
System.out.println("thread Run!!");
}
public static void main(String[] args) {
Sample sm = new Sample();
sm.start(); // start()로 스레드를 실행합니다.
}
}
Sample 클래스가 Thread 클래스를 상속했습니다. Thread 클래스의 run 메서드를 구현하면 위 코드 예제와 같이 sample.start() 실행 시 sample 객체의 run 메서드가 수행됩니다.

@ Thread 클래스를 extends 했기때문에 start 메서드 실행 시 run 메서드가 수행합니다. Thread 클래스는 start 실행 시 run 메서드가 수행되도록 내부적으로 동작합니다.
위 예제를 실행하면 "thread Run!!" 이라고 문장이 출력이 될 것이다. 스레드가 하나인 경우에는 도애체 스레드가 어떻게 동작하고 있는지 명확하지가 않습니다.
밑에 예제를 통해서 확인을 해보자
public class Sample extends Thread{
int seq;
public Sample(int seq) {
this.seq = seq;
}
@Override
public void run() { //Thread를 상속하면 run 메서드를 구현해야 합니다.
System.out.println(this.seq + " thread start!!"); //스레드 시작
try {
Thread.sleep(2000); //2초간 대기한다
} catch (InterruptedException e) {
}
System.out.println(this.seq + " thread end!!"); //스레드 종료
}
public static void main(String[] args) {
for(int i =0; i<10; i++){ // 총 10개의 스레드를 생성하여 실행한다
Thread t = new Sample(i);
t.start();
}
System.out.println("main end"); //main 메소드 종료합니다
}
}
총 10개의 스레드를 실행시키는 예제이다. 어떤 스레드인지 확인하기 위해서 스레드마다 생성자에 순번을 부여했다. 그리고 스레드 메서드(run) 수행 시 시작과 종료를 출력하게 되었고, 시작과 종료 사이에 2초 간격이 생기도록 작성하였습니다.
그리고 main 메소드 종료 시 "main end"를 출력하도록 했습니다.
결과는

0번 스레드부터 9번 스레드까지 순서대로 실행되지 되지 않고 그 순서가 일정치 않은 것을 보면 스레드는 순서에 상관없이 동시에 실행된다는 사실을 알 수가 있습니다. 그리고 스레드가 종료되기 전에 main 메서드가 종료되었다는 사실이다. main 메서드 종료 시 "main end"라는 문자열이 출력되는데 위 결과에서는 제일 처음에 출력이 되어있습니다.
●스레드의 우선순위
자바에서 각 스레드는 우선순위(prioririty)에 관한 자신만의 필드를 가지고 있습니다.
이러한 우선순위에 따라 특정 스레드가 더 많은 시간 동안 작업을 할 수 있도록 설정할 수 있습니다.
| 필드 | 설명 |
| static int MAX_PRIORITY | 스레드가 가질 수 있는 최대 우선순위를 명시함 |
| static int MIN_PRIORITY | 스레드가 가질 수 있는 최소 우선순위를 명시함 |
| static int NORM_PRIORITY | 스레드가 생성될 때 기본 우선순위를 명시함 |
우선 순위는 1부터 10까지의 숫자로 설정할 수 있으며, 다음 3개의 static 변수를 제공합니다.
스레드를 생성하면 기본적으로 우선순위는 5로 설정되어있습니다. 어떤 스레드가 5보다 큰 우선순위를 갖고 있다면, 시스템은 우선순위가 5인 스레드보다 더 우선하여 CPU를 스케줄링해줍니다. 만약 두 개의 스레드가 우선순위가 같다면, 어떤 스레드가 우선시할지 알 수 없습니다. 우선순위가 10인 스레드가 우선순위가 1인 스레드보다 10배 더 빨리 수행되는 것이 아닙니다. 단지 우선순위가 10인 스레드는 우선순위가 1인 스레드보다 좀 더 많이 실행 큐에 포함되어, 좀 더 많은 작업 시간을 할당받을 뿐입니다.
Thread에 우선순위를 설정 할 때는 setPriority()를, 설정된 값을 확인할 때는 getPriority()를 호출합니다.
class ThreadWithRunnable implements Runnable{
@Override
public void run() {
for(int i = 0; i< 5; i++){
System.out.println(Thread.currentThread().getName()); // 현재 실행 중인 스레드의 이름을 반환함
}
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class ThreadPriorityTest {
public static void main(String[] args) {
Thread thread1 = new Thread(new ThreadWithRunnable());
Thread thread2 = new Thread(new ThreadWithRunnable());
thread2.setPriority(10); //Thread-1의 우선순위를 10으로 변경함
thread1.start(); //Thread-0 실행
thread2.start(); //Thread-1 실행
System.out.println(thread1.getPriority());
System.out.println(thread2.getPriority());
}
}
main() 메서드를 실행하는 스레드의 우선순위는 언제나 5이므로, main() 메서드 내에서 생성된 스레드 Thread-0이 우선순위는 5로 설정되는 것을 확인할 수 있습니다.

밑에 예제의 결과는 Thread-0이 먼저 실행되고, Thread-1 나중에 실행됩니다. 우선순위를 정해주는 라인이 없으면 따라서 Thread-0이 먼저 실행되고, Thread-1이 나중에 실행이 될 것입니다.
●Join
위 예제를 보면 스레드가 모두 수행되고 종료되기 전에 main 메서드가 먼저 종료되어 버렸다. 그렇다면 모든 스레드가 종료된 후에 main 메서드를 종료시키고 싶은 경우에는 어떻게 해야 될까요?
밑에 예제를 보자
import java.util.ArrayList;
public class Sample extends Thread {
int seq;
public Sample(int seq) {
this.seq = seq;
}
public void run() {
System.out.println(this.seq+" thread start!!");
try {
Thread.sleep(1000);
}catch(Exception e) {
}
System.out.println(this.seq+" thread end!!");
}
public static void main(String[] args) {
ArrayList<Thread> threads = new ArrayList<>();
for(int i=0; i<10; i++) {
Thread t = new Sample(i);
t.start();
threads.add(t);
}
for(int i=0; i<threads.size(); i++) {
Thread t = threads.get(i);
try {
t.join(); // t 쓰레드가 종료할 때까지 기다린다.
}catch(Exception e) {
}
}
System.out.println("main end!!");
}
}
생성된 스레드를 담기 위해서 ArrayList 객체인 threads를 만든 후 스레드 생성 시 객체를 threads에 저장했다. 그리고 main 메서드가 종료되기 전에 threads에 담긴 각각의 thread에 join 메서드를 호출하여 스레드가 종료될 때까지 대기하도록 했다. 스레드의 join 메서드는 스레드가 종료될 때까지 기다리게 하는 메서드입니다.

"main end!!"라는 문자열이 가장 마지막 줄에 출력되는 것을 확인할 수 있습니다.
스레드 프로그래밍 시 가장 많은 실수하는 부분이 바로 스레드가 종료되지 않았는데 스레드가 종료된 줄 알고 그다음 로직을 수행하게 만드는 일이다. 스레드가 종료된 후 그 다음 로직을 수행해야 할 때 꼭 필요한 메서드 join을 꼭 기억하세요
●Runnable
보통 스레드 객체를 만들 때 위에 예체 럼 Thread를 상속하여 만들기도 하지만 보통은 Runnable 인터페이스를 구현하도록 하는 방법을 주로 사용합니다. 이유는 Thread 클래스를 상속하면 다른 클래스를 상속할 수 없기 때문입니다.
위에서 만든 예제를 Runnable 인터페이스를 사용하는 방식으로 변경해봅시다
import java.util.ArrayList;
public class Sample implements Runnable {
int seq;
public Sample(int seq) {
this.seq = seq;
}
public void run() {
System.out.println(this.seq+" thread start!!");
try {
Thread.sleep(1000);
}catch(Exception e) {
}
System.out.println(this.seq+" thread end!!");
}
public static void main(String[] args) {
ArrayList<Thread> threads = new ArrayList<>();
for(int i=0; i<10; i++) {
Thread t = new Thread(new Sample(i));
t.start();
threads.add(t);
}
for(int i=0; i<threads.size(); i++) {
Thread t = threads.get(i);
try {
t.join(); // t 쓰레드가 종료할 때까지 기다린다.
}catch(Exception e) {
}
}
System.out.println("main end!!");
}
Thread를 extends 하던 것에서 Runnable을 implements 하도록 변경되었습니다.
※ Runnable 인터페이스는 run 메서드를 구현하도록 강제한다.
그리고 Thread를 생성하는 부분을 다음과 같이 변경했습니다.
Thread t = new Thread(new Sample(i));
Thread의 생성자로 Runnable 인터페이스를 구현한 객체를 넘길 수 있는데 이 방법을 사용한 것입니다. 이렇게 변경된 코드는 이 전에 만들었던 예제와 완전히 동일하게 동작한다. 다만 인터페이스를 이용했으니 상속 및 기타 등등에서 좀 더 유연한 프로그램으로 발전했다고 볼 수 있습니다.
※참고※
https://wikidocs.net/230
http://www.tcpschool.com/java/java_thread_concept
'java' 카테고리의 다른 글
| [JAVA 기초] Wrapper Class 란? (0) | 2023.06.29 |
|---|---|
| Java - Optional 문법 (0) | 2022.08.12 |
| ThreadLocal 이란? (0) | 2022.07.28 |
| [Java] 컬렉션 - Collection 이란? (0) | 2022.07.18 |
| [Java] Static import 란? (0) | 2022.07.14 |