Java - Thread

Thread에 대해서

Posted by Yan on March 20, 2021

쓰레드

  • 프로세스와 자원을 이용해서 실제로 작업을 수행하는 것
  • 모든 프로세스에는 최소한 하나 이상의 쓰레드 존재. 둘 이상의 쓰레드를 가진 프로세스 == multi-threaded process
  • 하나의 프로세스가 가질 수 있는 쓰레드의 개수는 제한되어 있지 않지만, 쓰레드가 작업을 수행하면서 개별적인 호출스택을 필요로 하기 때문에 메모리의 한계에 따라 생성할 수 있는 쓰레드 수가 결정된다.

프로세스

  • 실행 중인 프로그램을 뜻함
  • 프로그램을 실행하면 OS로부터 실행에 필요한 자원을 할당받아 프로세스가 된다.
  • 필요한 데이터와 메모리 등의 자원, 쓰레드로 구성

멀티쓰레딩

  • 하나의 프로세스 내에서 여러 쓰레드가 동시에 작업을 수행하는 것
  • CPU의 코어가 한 번에 단 하나의 작업만 수행할 수 있기 때문에, 실제로 동시에 처리되는 작업의 개수는 코어의 개수와 일치.
  • 쓰레드 수는 코어의 개수보다 언제나 많기 때문에 각 코어가 아주 짧은 시간동안 여러 작업을 번갈아 가며 수행하는 것이 동시에 수행되는 것처럼 보이는 것임.

멀티쓰레드의 장점

  • CPU의 사용률을 향상시킨다
  • 자원을 보다 효율적으로 사용할 수 있다
  • 사용자에 대한 응답성이 향상된다
  • 작업이 분리되어 코드가 간결해진다

멀티쓰레드 예시

  • 메신저로 채팅을 하면서 음악을 듣거나 통화를 할 수 있는 것
  • 싱글 쓰레드라면 한 가지 일만 처리할 수 있다.
  • 여러 사용자에게 서비스를 해주는 서버 프로그램일 경우 멀티쓰레드는 필수적. 하나의 서버 프로세스 -> 여러 개 쓰레드 생성 -> 사용자의 요청이 일대일로 처리되도록 만듦
  • 싱글쓰레드라면 사용자의 요청마다 새로운 프로세스를 생성해야하는데, 프로세스를 만드는 것이 쓰레드를 생성하는 것보다 시간과 공간을 많이 사용(사용자의 수가 많으면 서비스할 수 없게 됨)

멀티쓰레드 사용시 주의해야할 점

  • 여러 쓰레드가 같은 프로세스 내에서 자원을 공유하면서 작업을 하기 때문에 synchronization, deadlock교착상태 같은 문제가 발생할 수 있다.
  • 교착상태 : 두 쓰레드가 자원을 점유한 상태에서 서로 상대편이 점유한 자원을 사용하려고 기다리느라 진행이 멈춰있는 상태

쓰레드 구현

  1. Thread클래스 상속 다른 클래스를 상속받을 수 없게 된다.
1
2
3
4
5
class MyThread extends Thread {
    public void run() {

    }
}
  1. Runnable interface 구현
  • 일반적으로 사용하는 방법
  • 재사용성이 높고 코드의 일관성을 유지할 수 있어서 보다 객체지향적인 방법이다.
1
2
3
4
5
6
7
8
9
10
class MyThread implements Runnable {
  //Runnable 인터페이스: 오로지 run()만 정의되어 있는 간단한 인터페이스
    public void run() {

    }
}

public interface Runnable {
  public abstract void run();
}

쓰레드 구현 예시

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class ThreadEx1 {
  public static void main(String args[]) {
    ThreadEx1_1 t1 = new ThreadEx_1();

    Runnable r = new ThreadEx1_2();
    Thread t2 = new Thread(r);
    //Runnable 인터페이스를 구현한 클래스의 인스턴스 생성
    //그 인스턴스를 Thread클래스의 생성자 매개변수로 제공해야함

    t1.start();
    t2.start();
  }
}

class ThreadEx1_1 extends Thread {
  public void run(){
    for (int i = 0; i<3; i++) {
      System.out.println(getName()); // 조상 Thread의 getName 호출
    }
  }
}

class ThreadEx1_2 implements Runnable {
  public void run() {
    for (int i=0; i<3; i++) {
      System.out.println(Thread.currentThread().getName());//현재 실행중인 Thread의 getName호출
    }
  }
}

// Thread-0
// Thread-0
// Thread-0
// Thread-1
// Thread-1
// Thread-1

start()와 run()의 차이

run()

main method에서 run() -> 생성된 쓰레드를 실행시키는 것이 아니라 단순히 클래스에 선언된 메소드를 호출하는 것

start()

새로운 쓰레드가 작업을 실행하는데 필요한 call stack을 생성 -> run()을 호출 -> 생성된 call stack에 run()이 첫번째로 올라가게 함

main thread

  • main method작업을 수행하는 것도 thread.
  • 실행 중인 사용자 thread가 하나도 없을 때 프로그램은 종료됨

single thread와 multi thread의 차이

  • 하나의 쓰레드로 두 작업을 처리하는 경우 : 한 작업을 마친 후에 다른 작업 시작함

  • 두 쓰레드로 작업하는 경우 : 짧은 시간 동안 2개의 쓰레드를 번갈아 가면서 작업 수행 -> 동시에 두 작업이 처리되는 것과 같이 느끼게 함

  • 작업시간은 하나의 쓰레드와 두개의 쓰레드 경우가 비슷한데, 쓰레드 간의 작업 전환(context switching)에 시간이 걸리기 때문