스레드(Thread)

Jimin·2022년 8월 23일
0

JAVA

목록 보기
16/23

프로세스란?

  • 프로세스는 실행중인 프로그램을 의미한다.
  • 프로세스는 완전히 독립적인 수행 단위이다.

스레드란?

  • 스레드는 프로그램 내에서 실행되는 프로그램 제어흐름(실행단위)을 말한다.
  • 한 프로그램에 여러 개의 스레드가 존재할 수 있다.
    스레드가 1개라면 단일 스레드, 2개 이상이라면 다중 스레드라고 한다.
  • 프로그램 코드를 한 줄씩 실행하는 것이 스레드의 역할이다. (= 실행제어)
  • 스레드란 곧 프로그램을 실행하는 주체이다.

멀티스레드란?

  • 멀티스레드 = 다중스레드
  • 멀티스레드는 여러 개의 스레드를 이용하는 프로그램이다.
  • 다중 스레드에서 각각의 스레드는 하나의 독립적인 프로세스처럼 작업을 수행한다.

비유

여러 CPU: 요리사
여러 프로세스: 라면 조리대, 피자 조리대, 자장면 조리대
여러 스레드: 4개의 라면 버너, 5개의 피자 화덕, 3개의 자장면 가스


멀티스레드로 구현된 프로그램을 실행하면,

  • 하나의 프로세서는 한 번에 한 개의 스레드밖에 실행시키지 못한다.
  • 대신 일정한 시간 간격으로 수행해야하는 스레드를 전환한다.
  • 스레드를 전환할 때는 운영체제 스케줄러의 기준에 따라 순서가 정해지게 된다.
  • 여러 스레드를 번갈아 처리하기 때문에 엄밀히 한 번에 한 가지를 처리하지만, 동시에 작업한는 듯한 효과를 준다.
  • 이와 같은 방식을 시분할 방식이라고 한다.

멀티스레드 방식을 사용하는 이유?
(feat.멀티스레드의 장점)

  • 프로세스는 독립적이다. 이 때문에 작업공간이 독립적이고, 프로세스끼리 자원 및 데이터를 공유하기 어렵다.
    ⇒ 따라서 만약 프로세스간의 데이터 전송이 필요한 경우, 자원 소요가 많다.
  • 프로그램 내의 스레드는 서로 독립적이지 않다. 작업공간을 같이 사용하기 때문에 자원 및 데이터를 공유할 수 있다.
    ⇒ 따라서 스레드간 데이터 전송이 필요한 경우 시간, 자원 소요가 적다.
  • 정리: 스레드는 프로세스처럼 작업을 동시에 처리할 수 있는 공통적인 특징이 있으면서, 프로세스보다 오버헤드가 적다는 것이 장점이다.
    (그러니까 다중 프로세서보다는 다중 스레드가 낫다는 뜻인듯, 프로세서 여러개로 여러개 작업하는 것보다 스레드 여러개로 작업하는 것이 낫다.)

멀티 프로세싱 vs. 멀티 태스킹 vs. 멀티 스레드

용어멀티프로세싱
(=multi processing)
멀티태스킹
(= multi tasking)
멀티스레딩
(= multi threading)
관점시스템 관점프로그램 외부에서의 관점프로그램 내부에서의 관점
의미CPU 여러개에서
동시에 여러개의 프로세스 수행
CPU 1개에서 동시에
여러 프로그램 실행
Processor 1개가 동시에
여러 스레드 실행
예시다수의 송금 거래를 동시에 처리하는 은행전산 시스템PC 카톡 켜놓고
YouTube 음악 들으면서
온라인 뱅킹 업무
프로그램 안에서 실행되는 코드의 흐름이 여러개

멀티스레드를 적용하기 위한 조건

  1. 병행성(concurrency): 다수의 스레드 생성 방법 존재
  2. 동기화(synchronization): 작업이 방해 받지 않고 각 스레드의 동기화 방법 존재
  3. 통신(communication): 서로 다른 스레드가 정보를 교환할 수 있는 방법이 존재

스레드 생성하기

스레드를 생성하는 방법은 두 가지이다.
1. Thread 클래스를 상속 받아서 생성
2. Runnable 인터페이스 구현

  1. Thread 클래스를 상속 받아서 생성
    • 스레드 생성
      • java.lang.Thread 클래스를 상속받아서 run() 메서드 오버라이딩
    • 특징
      • 실행 스레드로 자신의 콜 스택을 갖춘 독립적인 프로세스
      • start() 메서드를 통해 스레드가 시작된다.
  2. Runnable 인터페이스 구현
    • 스레드 생성
      • java.lang.Runnable 인터페이스로부터 run() 메서드를 구현하여 생성한다.
        (참고로 Runnable 인터페이스는 run() 메서드 1개만 가지는 함수형 인터페이스이다.)
    • 특징
      • Runnable 인터페이스를 구현한다고 해서 바로 스레드가 되지 않는다.
      • Thread class를 통해 스레드가 될 수 있다.
      • 방법: 객체 참조 변수를 인자 값으로 하는 Thread 생성

방법이 두 가지인 이유는 자바가 단일 상속만 허용하기 때문이다. 만약 상속받아야 하는 클래스가 있다면 Thread 클래스를 상속 받는 방법 대신 Runnable 인터페이스를 구현하는 방법을 사용할 수 있다.

생성자내용
Thread()일반적인 스레드 객체 생성
Thread-n 이런 이름을 가진 스레드가 만들어진다.
Thread(Runnable target)run() 메서드를 가지는 객체를 인자값으로 할당하기
Thread(Runnable target, String name)run() 메서드를 가지는 객체와 스레드 이름을 인자값으로 할당하기
Thread(String name)스레드 생성하면서 스레드 이름 지어주기
  • Thread 클래스 상속 받아 thread 생성하기
public class Test01 extends Thread{
    @Override
    public void run() {
		/* 스레드 실행코드 */
    }
}
  • Runnable 인터페이스 구현하여 thread 생성하기
public class Test01 implements Runnable {
    @Override
    public void run() {
		/* 스레드 실행코드 */
    }
}

스레드의 생성주기

1. Runnable(준비 상태)

스레드가 실행되기 위한 준비단계.
CPU를 점유하고 있지 않으며 실행(Running 상태)을 하기 위해 대기하고 있는 상태.
코딩 상에서 start() 메소드를 호출하면 run() 메소드에 설정된 스레드가 Runnable 상태로 진입한다.
"Ready" 상태라고도 한다.

2. Running (실행 상태)

CPU를 점유하여 실행하고 있는 상태.
run() 메서드는 JVM만이 호출 가능하다.
Runnable(준비 상태)에 있는 여러 스레드 중 우선 순위를 가진 스레드가 결정되면 JVM이 자동으로 run() 메서드를 호출하여 스레드가 Running 상태로 진입한다.

3. Dead (종료 상태)

Running 상태에서 스레드가 모두 실행되고 난 후 완료 상태.
"Done" 상태라고도 한다.

4. Blocked (지연 상태)

CPU 점유권을 상실한 상태.
후에 특정 메서드를 실행시켜 Runnabl(준비상태)로 전환한다.
wait() 메서드에 의해 Blocked 상태가 된 스레드는 notify() 메소드가 호출되면 Runnable 상태로 돌아간다.
sleep(시간) 메서드에 의해 Blocked 상태가 된 스레드는 지정된 시간이 지나면 Runnable 상태로 돌아간다.


start()와 run()

start()run()
새로운 Thread가 생성되며 Thread가 시작되면
run() 메서드가 실행된다.
Thread가 생성되지 않으며 그냥 run() 메서드만 실행된다.
동일한 객체에서 두 번 이상 호출시
IllegalThreadStateException 예외가 발생한다.
호출 수에 제한 없이 계속 호출할 수 있다.
멀티스레드로 동작한다.싱글스레드로 동작한다.

runnable에는 start() 메서드가 없다.
thread의 start() 메서드가 runnable의 run() 메서드를 실행시켜주는 것이다.

Thread와 Runnable 동작 과정

Thread.start() 호출 → Thread 생성 → Thread가 Runnable의 run() 실행


Thread와 Runnable 그리고 익명 클래스 연습

Thread 연습

public class ExamThread {

  public static void main(String[] args) {
    int count = 1000;

    // 1단계
    class MyThread1 extends Thread{
      @Override
      public void run() {
        for (int i = 0; i < count; i++) {
          System.out.println("==> " + i);
        }
        super.run();
      }
    }

    MyThread1 thread = new MyThread1();
    thread.start();

    // 2단계
    class MyThread2 extends Thread{
      @Override
      public void run() {
        for (int i = 0; i < count; i++) {
          System.out.println("==> " + i);
        }
        super.run();
      }
    }
    new MyThread2().start();

    // 3단계
    new Thread() {
      @Override
      public void run() {
        for (int i = 0; i < count; i++) {
          System.out.println("==> " + i);
        }
        super.run();
      }
    }.start();

    for (int i = 0; i < count; i++) {
      System.out.println(">>> " + i);
    }
  }
}

Runnable 연습

public class ExamRunnable {

  public static void main(String[] args) {

    int count = 1000;

    // 1단계
    class MyRunnable1 implements Runnable{
      @Override
      public void run() {
        for (int i = 0; i < count; i++) {
          System.out.println("==> " + i);
        }
      }
    }
    MyRunnable1 r = new MyRunnable1();
    Thread t1 = new Thread(r);
    t1.start();

    // 2단계
    class MyRunnable2 implements Runnable{
      @Override
      public void run() {
        for (int i = 0; i < count; i++) {
          System.out.println("==> " + i);
        }
      }
    }
    Thread t2= new Thread(new MyRunnable2());
    t2.start();

    // 3단계 - 스레드 변수는 재사용할 수 없으므로 변수로 만들어줄 필요 없다.
    class MyRunnable3 implements Runnable{
      @Override
      public void run() {
        for (int i = 0; i < count; i++) {
          System.out.println("==> " + i);
        }
      }
    }
    new Thread(new MyRunnable3()).start();

    // 4단계 -Runnable 안의 코드의 길이가 짧은 편이므로 익명 클래스로 만들어준다.
    new Thread(new Runnable() {
      @Override
      public void run() {
        for (int i = 0; i < count; i++) {
          System.out.println("==> " + i);
        }
      }
    }).start();

    // 5단계 - 인터페이스 안에 함수가 한 개인 형태이므로 람다 문법을 사용한다.
    new Thread(
        () -> {
          for (int i = 0; i < count; i++) {
            System.out.println("==> " + i);
          }
        }
        ).start();



    for (int i = 0; i < count; i++) {
      System.out.println(">>> " + i);
    }
  }
}

출처

https://makecodework.tistory.com/entry/Java-%EC%8A%A4%EB%A0%88%EB%93%9CThread-%EC%8A%A4%EB%A0%88%EB%93%9C-%EA%B0%9C%EB%85%90-%EB%B0%8F-%EC%83%9D%EC%84%B1%ED%95%98%EA%B8%B0

스레드의 생명주기 잘 설명해 놓은 블로그: https://coding-factory.tistory.com/279


profile
https://github.com/Dingadung

0개의 댓글