[Java] 스레드(thread) - run, start, I/O블락킹, setPriority

2024. 6. 18. 19:51WebBack/Java

스레드란?


스레드는 프로세스를 구성하는 실행의 흐름 단위

 

프로세스는 여러 개의 스레드를 가질 수 있습니다. 스레드를 이용하면 하나의 프로세스에서 여러 부분을 동시에 실행할 수 있습니다. 

 

멀티 프로세스 vs  멀티스레드


 

멀티프로세스는 여러 프로세스를 동시에 실행하는 것을 말하고, 멀티스레드는 여러 스레드로 프로세스를 동시에 실행하는 것을 말한다. <<혼자 공부하는 컴퓨터구조 운영체제, p. 307>>

 

위의 설명으로는 큰 차이가 없는 것처럼 보이지만, 양자에는 중요한 차이점이 존재한다.

 

프로세스는 메모리의 코드 영역, 데이터 영역, 힙 영역 등을 비롯한 모든 자원이 복제되어 메모리에 적재된다. 

스레드는 코드 영역, 데이터 영역, 힙 영역 등을 비롯한 주요 자원을 공유한다. 

 

스레드는 자원 공유를 통해서 '메모리를 더 효율적으로 사용할 수 있다'라는 장점을 가진다. 

반면에, 자원을 공유하기 때문에 '동기화와 교착상태'같은 문제가 발생할 수 있다는 단점을 가진다. 

 

자바 코드를 실행하면서 어떤 문제가 발생하는지 살펴보자. 

 

자바에서 스레드는 어떻게 구현할 수 있나?


스레드를 구현하는 방법은 크게 두 가지이다. 

 

1. Thread클래스를 상속받는 방법 

2. Runnable인터페이스를 구현하는 방법

 

1번 방법은 다음과 같이 사용한다. 

//Thread클래스 run()을 오버라이딩

class MyThread extends Thread {
	public void run() {
    //작업내용
    }
}

/*중략*/

MyThread t = new MyThread();
t.start();

 

2번 방법은 다음과 같이 사용한다. 

class MyThread implements Runnable {
	public void run() {
    //작업 내용
    }
}

/* 중략 */

Runnable r = new MyThread();
Thread t = new Thread(r); // 생성자 매개변수로 r을 줌
t.start();

 

어느 쪽이든 run()의 몸통{}을 구현한다는 점에서는 동일하다. 

 

스레드의 단점은?


 

public class Ex13_1 {
  public static void main(String[] args) {
    ThreadEx1_1 t1 = new ThreadEx1_1();
    t1.start();

    Runnable r = new ThreadEx1_2();
    Thread t2 = new Thread(r);
    t2.start();
  }
}

class ThreadEx1_1 extends Thread{
  public void run(){
    for(int i = 0; i < 100; i++){
      System.out.print(0);
    }
  }
}

class ThreadEx1_2 implements Runnable {
  public void run(){
    for(int i = 0; i < 100; i++){
      System.out.print(1);
    }
  }
}

 

1번 방법과 2번 방법을 동시에 실행하였다. 기존의 코드에서는 t1.start()와 t2.start()가 순차적으로 실행되었다. 하지만, 스레드를 사용하면 t1.start()와 t2.start()가 동시에 실행된다. 그 결과를 아래를 통해 확인할 수 있다. 

00000000000000000000000000000000000000000000000000000000000000000001111111100000001111100000000000000000000000000111111111111111111111111111111111111111111111111111111111111111111111111111111111111111

 

 

스레드의 특징은 어떤 코드가 먼저 실행될 지 알 수 없다. (예측은 가능하다) 

게다가 두 코드가 모두 같은 자원을 이용하기 때문에 결과가 섞여서 나온다. 

그리고 규칙성이 존재하지 않다는 것도 중요하다. 실행 순서는 OS의 스케쥴러에 따른다. 

 

왜 run이 아니라 start를 사용할까?


1. run()을 구현했지만, 코드에서는 start()를 사용한다. 이는 start()가 새로운 호출 스택을 생성하여 거기에 run()을 호출하기 때문이다. 

2. start()는 한 번만 호출할 수 있다. 다시 실행하면 IllegalThreadStateException이 발생한다. 

3. 만약 다시 호출하고 싶으면 아래처럼 새로운 스레드를 생성한 뒤에 호출해야한다. 

ThreadEx1_1 t1 = new ThreadEx1_1();
t1.start();

t1 = new ThreadEx1_1();
t1.start();

 

start()는 호출스택을 생성하여 그 다음에 run()을 호출하여 생성된 호출 스택에 run()이 첫 번째로 올라가게 한다. 

즉, start()를 실행해야 main 스레드와 run()이 독립된 공간에서 작업을 수행할 수 있게 된다. 

 

참고로, main메서드가 수행을 마쳤더라도 다른 스레드가 작업중이면 프로그램은 종료되지 않는다. 

 

멀티스레드는 싱글스레드보다 느릴 수 있다?


멀티스레드는 동시에 작업이 처리되는 것처럼 보이지만, 실제로는 작업을 번갈아 작업을 수행한다. 그래서 싱글스레드와 멀티스레드의 속도는 거의 차이가 나지 않고, 오히려 단순 작업의 경우에는 싱글스레드가 작업 속도가 더 빠를 수 있다. 왜냐하면, 스레드가 작업을 번갈아하기 전에 작업 전환(context switching)이 이루어지기 때문이다. 

 

멀티스레드는 그럼 언제 효율적일까?


그러면 멀티스레드를 사용하면 얻는 장점은 무엇일까? 바로 서로 다른 자원을 사용하는 작업의 경우에 높은 효율을 주기 때문이다.

 

예를들어, 작업 A와 작업 B 사이에 사용자로부터 무언가 입력을 받는다고 가정하자. 싱글스레드의 경우에는, 작업 B가 실행되지 못하고 입력을 기다릴 수 밖에 없다. 반면에, 멀티스레드는 작업 B가 입력과 별개로 작업시 수행될 수 있어서 싱글보다 훨씬 짧은 시간에 작업을 마칠 수 있다. 

public class Ex13_5 {
  public static void main(String[] args) {
    ThreadEx5_1 th1 = new ThreadEx5_1();
    th1.start();

    String input = JOptionPane.showInputDialog("아무 값이나 입력하세요");
    System.out.println("입력하신 값은 " + input + "입니다");
  }
}

class ThreadEx5_1 extends Thread {
  public void run(){
    for(int i=10; i > 0; i--){
      System.out.println(i);
      try {
        sleep(1000);
      } catch(Exception e) {}
    }
  }
}

 

위는 이를 보여주는 코드 중의 하나이다. 

JOptionPane.showInputDialog로 인해 아래와 같은 입력창이 뜬다. 

그런데 입력을 하는 동안 ThreadEx5_1 스레드가 실행되어 카운트 다운을 한다. 

10
9
8
7
6
입력하신 값은 가나다입니다
5
4
3
2
1

 

싱글 스레드였으면 사용자가 입력을 할 때까지 카운트 다운이 실행되지 않았을 것이다. 

 

스레드에 우선순위를 부여하기

void setPriority(int newPriority) 스레드에 우선순위 부여 
int getPriority() 스레드의 우선순위를 반환

 

자바에서는 코드에 우선순위를 부여할 수 있다. 값은 1~10까지 부여가 가능하고, 높은 숫자를 부여받을수록 우선순위가 높다. 만약, 따로 지정하지 않았으면 우선순위는 5(기본값)이다. 

 

하지만!!

스레드의 작동은 자바 가상 머신(JVM)보다 OS의 스케쥴러에 더 영향을 받아서 생각보다 큰 효과가 없을 수 있다.