Programming/JAVA

JAVA 스레드 ExecutorService 개요

khj1999 2024. 10. 22. 21:39

Java의 Executor 서비스는 멀티스레딩을 관리하고, 스레드 풀을 생성하여 작업을 실행하는 간편한 방법을 제공. Executor 프레임워크는 스레드를 직접 생성하고 관리하는 대신, 스레드를 재사용할 수 있는 방식으로 효율적인
작업 처리를 가능하게 한다. 이로 인해 코드가 간결해지고, 성능이 향상된다.

1. Executor 프레임워크 개요

Java의 Executor 프레임워크는 다음과 같은 구성 요소로 이루어져 있다:

  • Executor 인터페이스: 기본적인 작업 실행 메서드를 정의. (execute(Runnable command))
  • ExecutorService 인터페이스: Executor의 하위 인터페이스로
    종료 및 작업 제출과 같은 추가 기능을 제공. (submit(), shutdown(), invokeAll(), 등)
  • ThreadPoolExecutor 클래스: ExecutorService의 구현체로, 스레드 풀을 사용하여 작업을 처리
  • ScheduledExecutorService 인터페이스: 특정 작업을 지연시키거나 주기적으로 실행할 수 있는 기능을 제공

2. ExecutorService 사용법

ExecutorService를 사용하여 스레드 풀을 생성하고 작업을 제출하는 기본적인 방법은 다음과 같다:

2.1. 스레드 풀 생성

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ExecutorServiceExample {
    public static void main(String[] args) {
        // 스레드 풀 생성 (2개의 스레드를 가진 고정 스레드 풀)
        ExecutorService executorService = Executors.newFixedThreadPool(2);

        // 작업 제출
        for (int i = 0; i < 5; i++) {
            final int taskId = i;
            executorService.submit(() -> {
                System.out.println("작업 " + taskId + " 실행 중 - 스레드: " + Thread.currentThread().getName());
                try {
                    Thread.sleep(1000); // 작업 수행
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
                System.out.println("작업 " + taskId + " 완료");
            });
        }

        // ExecutorService 종료
        executorService.shutdown();
    }
}

2.2. 작업 제출 및 반환 값 처리

submit() 메서드는 Future 객체를 반환하므로, 작업이 완료된 후 결과를 얻거나 예외를 처리할 수 있다.

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class ExecutorServiceWithReturn {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool();

        Future<Integer> future = executorService.submit(new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                // 작업 수행
                Thread.sleep(1000);
                return 42; // 결과 반환
            }
        });

        try {
            // 결과를 가져옴
            Integer result = future.get();
            System.out.println("작업 결과: " + result);
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        } finally {
            executorService.shutdown();
        }
    }
}

2.3. ScheduledExecutorService 사용

특정 작업을 지연시키거나 주기적으로 실행하고 싶을 때는 ScheduledExecutorService를 사용할 수 있다.

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class ScheduledExecutorServiceExample {
    public static void main(String[] args) {
        ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);

        // 2초 후에 작업 실행
        scheduler.schedule(() -> System.out.println("2초 후에 실행된 작업"), 2, TimeUnit.SECONDS);

        // 1초 간격으로 작업 실행
        scheduler.scheduleAtFixedRate(() -> System.out.println("주기적으로 실행되는 작업"), 0, 1, TimeUnit.SECONDS);
    }
}

3. 장점

  • 스레드 관리의 단순화: 스레드 생성과 관리를 Executor 서비스가 처리하므로 코드가 간결해지고
    재사용 가능성이 높진다.
  • 리소스 효율성: 스레드 풀을 사용하여 필요한 수만큼의 스레드를 유지함으로써
    불필요한 스레드 생성을 방지한다.
  • 작업 우선순위 및 시간 관리: ScheduledExecutorService를 통해 작업의 우선순위를 관리하고
    지연된 실행이나 주기적인 작업 수행이 가능하다.

4. 종료

ExecutorService를 종료할 때는 shutdown() 또는 shutdownNow() 메서드를 호출:

  • shutdown(): 현재 실행 중인 작업이 완료된 후 스레드를 종료.
  • shutdownNow(): 현재 실행 중인 작업을 중단하고, 대기 중인 작업을 취소.

결론

Java의 Executor 서비스는 멀티스레딩을 쉽게 구현할 수 있게 도와주며, 코드의 가독성과 효율성을 높여준다.
다양한 작업을 효과적으로 처리하기 위해 ExecutorServiceScheduledExecutorService를 활용하면 된다. 이를 통해 안정적이고 관리하기 쉬운 멀티스레딩 프로그램을 작성할 수 있다.