Java Thread (2)

정미·2022년 7월 24일
0

Computer Science

목록 보기
41/81

Thread Group

서로 관련이 있는 스레드를 하나의 그룹으로 묶어 다루기 위한 장치

  • 트리 형태로 연결된다.
```java
public class Application {
	public static void main(String[] args) {
		Thread thread1 = new Thread(new ThreadImplementsInterface());
		thread1.start();
		System.out.prinn(thread1.getThreadGroup());  // java.lang.ThreadGroup[name=main,maxpri=10]

		ThreadGroup group = new ThreadGroup("myThreads");
		group.setMaxPriority(7);

		Thread thread2 = new Thread(group, new ThreadImplementsInterface());  // 그룹 지정
		thread2.start();
		System.out.prinn(thread2.getThreadGroup());  // java.lang.ThreadGroup[name=myThreads,maxpri=7]
	}
}
```
  • 자신이 포함된 스레드 그룹이나 그 하위 그룹에는 접근할 수 있지만 다른 그룹에는 접근 불가능

Daemon Thread

주 스레드의 작업을 돕는 보조 스레드

  • 사용자가 직접 제어하지 않고 백그라운드에서 돌아가면서 작업하는 프로그램
  • 일반 스레드(Normal Thread)가 모두 종료되면 데몬 스레드도 더는 할 일이 없으므로 자동 종료
  • 일반 스레드와 생성, 실행 방법은 같다.
  • 주 스레드가 데몬이 될 스레드를 실행시키기 전에 setDaemon(true)로 데몬 설정
```java
Thread thread = new Thread(new ThreadImplementsInterface());
thread.setDaemon(true);
thread.start();
```
  • garbage collection, 화면 자동 갱신, 요청 처리, 리소스 청소(resource cleanup)와 같은 백그라운드 태스크 실행
  • 낮은 우선순위를 가진다.

garbage collector

프로그래머가 동적으로 할당한 메모리 중 더 이상 사용하지 않은 영역을 자동으로 찾아내어 해제해주는 데몬 스레드

  • 자동 메모리 관리
    • 메모리 관련 버그 발생 확률이 낮아짐
  • 가비지 컬렉터가 동작하는 동안 프로세서가 일시적으로 중지되어 성능 저하가 발생한다.

Thread Pool

작업 처리에 사용되는 스레드를 제한된 개수만큼 정해놓고 작업 큐에 들어오는 작업들을 하나씩 스레드가 맡아 처리하는 것

  • 스레드 제어 문제 해결 방법
  • JVM 옵션 제어가 아닌 어플리케이션이 선택적으로 사용
  • Tomcat 같은 웹서버에서 사용
    • 동시에 수천~수만개의 요청

장단점

장점

  1. 프로그램 성능 저하 방지
    • 스레드 생성 및 수거가 매번 발생한다면 메모리 할당에 소모되는 비용이 많이 든다.
    • JVM이 스레드 생성 개수를 제약하지 않기 때문에 계속 생성한다면 성능 저하와 메모리 고갈이 될 수 있다.
  2. 다수의 사용자 요청을 빠르게 처리
  3. context switching이 발생할 때 지연을 줄일 수 있다.

단점

  1. 너무 많은 스레드를 만들어둔다면 메모리 낭비가 심해진다.
    • 얼만큼의 스레드가 필요할지 예측하고 할당해야 한다.

동작 방식

  1. 사용자 요청을 작업큐에 넣고
  2. 스레드풀은 작업큐에 들어온 task를 미리 생성해놓은 thread에게 할당한다.
  3. 다 처리한 스레드는 다시 어플리케이션에게 결과값을 반환한다.

생성 및 종료

  • java.util.concurrent 패키지의 ExecutorService, Executors 사용

개념/용어

  • 초기 스레드 수
    • ExecutorService 객체가 생성될 때 기본적으로 생성되는 스레드 수
  • corePoolSize 코어 스레드 수
    • 스레드가 증가한 후 사용되지 않은 스레드를 스레드 풀에서 제거할 때 최소한으로 유지해야할 수
  • maximumPoolSize 최대 스레드 수
    • 스레드풀에서 관리하는 최대 스레드 수
    • 모든 코어 스레드가 사용 중이고 내부 큐가 가득차면 maximumPoolSize만큼 커질 수 있다.
  • keepAliveTime 유지 시간
    • 현재 스레드 개수가 corePoolSize보다 많으면서 해당 파라미터값보다 오래 하는 일이 없으면 제거된다.

생성

  1. ExecutorService service = Executors.newFixedThreadPool(int nThreads)

    • 초기 스레드 개수 = 0개, 코어 스레드 수와 최대 스레드 수 = nThreads개
    • 스레드풀의 스레드 개수만큼 작업 처리, 작업이 더 많아도 개수 유지
    • 일을 하지 않아도 스레드를 제거하지 않는다.
    • 일정량의 업무가 발생할 때
  2. ExecutorService service = Executors.newCachedThreadPool()

    • 초기 스레드 수와 코어 스레드 수 = 0개, 최대 스레드 수 = Integer.MAX_VALUE
    • 스레드 개수보다 작업 개수가 많으면 스레드를 새로 생성하여 작업 처리
    • 60초 동안 아무 일도 하지 않는다면 스레드 종료 후 스레드풀에서 제거
  3. ExecutorService service = Executors.newScheduledThreadPool(int corePoolSize)

    • 스레드를 일정시간이 지나고 난 뒤 실행하도록 하는 스케줄링 스레드

종료

  • 스레드풀에 속한 스레드는 데몬 스레드가 아니다.
    • main 스레드가 종료돼도 작업을 처리하기 위해 계속 실행 상태로 남아있다.
    • main() 메서드 실행이 끝나도 어플리케이션 프로세스는 종료되지 않는다.
  • 스레드 풀을 강제 종료, 스레드 해체를 시켜주어야 한다.
  1. excutorService.shutdown()
    • 작업 큐에 남아있는 작업까지 모두 마무리 후 종료
    • 오버헤드를 줄이기 위해 많이 사용하는 방식
  2. executorService.shutdownNow()
    • 작업큐의 작업 잔량과 상관 없이 강제 종료
  3. executorService.awaitTermination(long timeout, TimeUnit unit)
    • timeout 시간 안에 모든 작업 처리를 하면 return true
    • 못하면 작업 스레드들을 interrupt()시킨 후 return false

작업 요청 방식

스레드풀에 작업 요청을 하는 방식

  1. execute()
    • 작업 처리 중 예외가 발생하면 해당 스레드 종료 후 스레드풀에서 제거
    • 새로운 스레드 생성하여 다른 작업 처리
    • 처리결과 반환 X
  2. submit()
    • 작업 처리 중 예외가 발생해도 스레드가 종료되지 않고 다음 작업에 사용됨
    • Future<?>로 처리 결과 반환
    • 더 바람직한 작업 요청 방식

출처

0개의 댓글