Language | Basic/Java

Java의 스레드(Thread) 알아보기

주정용 2021. 6. 2. 17:28
728x90

개요

  • 여러 가지 일을 병행하는 멀티태스킹은 프로세스 기반, 스레드 기반으로 구현할 수 있습니다.
    • 프로세스 기반
      • 여러 프로그램이 병행으로 실행되는 것
      • 프로세스가 생성될 때마다 새로운 메모리 영역을 할당받음
      • 프로세스 실행에 필요한 새로운 시스템 자원을 할당받음
    • 스레드 기반
      • 프로그램 내부에서 여러 작업이 병행으로 실행되는 것
      • 프로그램의 프로세스에서 사용하는 자원과 메모리를 공유
      • 스레드를 실행하기 위한 자원만 필요
      • 스레드 == 경량 프로세스
    • 멀티 스레드 환경에서는 모든 스레드가 종료되어야 프로그램이 종료됨

스레드의 목적

  • 멀티 스레드를 구현해야 하는 상황
    • 여러 사용자의 동시 요청 처리
    • 외부 데이터 IO 작업

스레드 활용

  • 스레드 생성
    • java.lang.Thread 클래스를 상속
    • Runnable 인터페이스를 구현해서 Thread를 생성할 때 매개변수로 주입
      • Thread 클래스도 Runnable 인터페이스를 구현한 클래스입니다.
  • Thread에서 수행할 작업은 run()에 구현해야 합니다.
  • Thread의 start()를 이용하여 run()을 호출하여 작업을 수행합니다.
  • start()를 실행하면 스레드는 실행 대기 상태가 되어 JVM의 스케줄링에 따라 작업을 수행합니다.
  • 스레드는 JVM의 스케줄링에 의해서 실행되기 때문에 여러 스레드의 작업 순서는 매번 달라집니다.
  • Runnable 인터페이스@FunctionalInterface가 선언되어 있는 함수형 인터페이스입니다.
    • 람다식을 이용해서 Runnable 객체를 구현할 수 있습니다.
  • Thread 클래스는 run()을 구현하고 있지 않습니다.
    • Runnable 인터페이스의 run()을 구현해서 스레드가 할 작업을 지정합니다.

스레드 설정

  • Thread의 start()는 스레드를 실행 대기 상태로 만들어줍니다.
  • 실행 대기 상태인 스레드는 메인 스레드는 main, 이외의 스레드는 Thread-번호의 형태로 이름이 지정됩니다.
    • 스레드에 대해서 디버깅 등 작업을 수행할 때 스레드를 쉽게 식별하기 위해서 setName()을 통해서 이름을 지정할 수 있습니다.
  • 스레드는 setPriority()를 통해서 우선순위를 지정할 수 있습니다.
    • 우선순위는 1 ~ 10 인 정수이며, 우선순위가 클수록(10) 더 많은 작업시간을 할당받습니다.
    • 우선순위를 지정하지 않으면 기본값인 5가 지정됩니다.
    • Thread 클래스에 선언된 우선순위 상수를 이용할 수 있습니다.
      • Thread.MAX_PRIORITY, Thread.MIN_PRIORITY, Thread.NORM_PRIORITY

스레드 간 동기화

  • 동기화
    • 공유 자원을 여러 스레드가 동시에 접근할 때 발생할 수 있는 오류를 방지하는 작업입니다.
  • 동기화 처리는 synchronized 키워드를 사용하여 블록, 메서드 단위로 수행할 수 있습니다.
    • 블록 동기화 : synchronized (객체 이름) { ... }
      • 어떤 스레드가 공유 객체에서 작업을 수행하고 있다면, 다른 스레드는 해당 작업이 끝날때까지 객체를 참조할 수 없습니다.
      • 공유 객체 이름을 지정해서 공유 객체를 동시 참조하여 발생할 수 있는 에러를 방지합니다.
      • 만일 클래스 자신이 공유된다면 this를 지정합니다.
    • 메서드 동기화 : synchronized returnType 메서드 이름(매개변수...) { ... }
      • 어떤 스레드가 해당 메서드를 호출하고 있다면, 다른 스레드는 해당 작업이 끝날때까지 메서드를 호출할 수 없습니다.

스레드 제어

  • 동시에 실행되는 스레드 수는 CPU가 몇개인지에 따라 결정됩니다.
    • CPU가 1개라면 특정 순간에 실행되는 스레드는 1개입니다.
    • 스케줄링에 따라 번갈아가며 스레드들이 작업을 수행하기 때문에 동시에 하는 것처럼 느껴집니다.

스레드 상태

Thread.state라는 enum 클래스를 통해서 스레드의 상태가 구분되어 있습니다.

  • NEW
    • 스레드 객체는 생성되었지만, 아직 start()가 호출되지 않은 상태
  • RUNNABLE
    • start()가 호출되어서 실행할 수 있는 상태
    • RUNNABLE일때 스레드는 JVM의 스케줄링에 의해 실행될 수 있음
  • BLOCKED
    • 실행 대기 상태
    • JVM에 의해 RUNNABLE로 변경될 수 있음
  • WAITING
    • 실행 대기 상태
    • 다른 스레드에 의해 RUNNABLE로 변경될 수 있음
  • TIME_WAITING
    • 실행 대기 상태
    • 일정 시간이 지나야 RUNNABLE로 변경될 수 있음
  • TERMINATED
    • 스레드 실행 종료 상태

스레드 제어 메서드

java.lang.object에 있습니다.

  • wait()
    • 호출한 스레드는 RUNNABLE --> WAITING
  • notify()
    • WAITING인 스레드 중에서 1개의 스레드만 RUNNABLE로 변경
  • notifyAll()
    • WAITING인 스레드 모두 RUNNABLE로 변경
  • join()
    • 스레드 간 종속관계를 지정
    • 실행중인 스레드는 다른 스레드의 join()을 만나면 join()을 호출한 스레드의 작업이 끝날때까지 대기
  • sleep()
    • 호출한 스레드를 지정된 시간동안 TIME_WAITING으로 변경
  • interrupt()
    • RUNNABLE인 스레드들의 실행을 중지

스레드 풀(Thread Pool)

  • 스레드의 수가 증가할수록 메모리 사용량이 늘고, 스케줄링 또한 복잡해집니다.
  • 스레드 풀은 이러한 난점을 극복하기 위해서 미리 스레드를 생성해놓고 재사용하는 방식입니다.
  • 재사용을 통해서 생성, 삭제 비용을 줄일 수 있습니다.
  • 스레드 수를 제한하여 스케줄링 오버헤드를 줄일 수 있습니다.

ExecutorService

  • 스레드 풀을 지원하는 인터페이스입니다.
  • Executors의 메서드를 사용해서 ExecutorService에 스레드 풀을 할당합니다.
    • newFixedThreadPool(int nThreads)
      • 매개변수로 받은 수만큼 스레드를 생성하고 관리합니다.
    • newCachedThreadPool()
      • 스레드 풀에 재사용할 수 있는 스레드가 있으면 재사용합니다.
      • 스레드가 없으면 스레드를 생성합니다.
      • 60초 동안 사용되지 않은 스레드는 삭제합니다.
  • 작업 실행
    • execute(Runnable runnable)
      • Runnable을 구현한 객체를 매개변수로 받아 스레드에 할당하고 작업을 수행합니다.
  • 스레드 풀 종료
    • 스레드 풀을 종료하지 않으면 프로그램이 종료되지 않습니다. 사용했으면 반드시 닫아야 합니다.
    • shutDown()
      • 현재 실행중인 스레드의 작업이 끝나면 스레드 풀을 닫습니다.
    • shutDownNow()
      • 실행중인 스레드의 작업을 즉시 종료하고 스레드 풀을 닫습니다.

'Language | Basic > Java' 카테고리의 다른 글

Java의 Garbage Collection  (0) 2021.05.28
JVM의 Heap Area  (0) 2021.05.28
JVM(Java Virtual Machine)이란  (0) 2021.05.28
Lambda와 함수형 인터페이스  (0) 2021.05.18
Stream API  (0) 2021.05.10