StatCounter - Free Web Tracker and Counter

Effective Java : 아이템 80. 스레드보다는 실행자, Task, Stream을 애용하라.

반응형

아이템 80. 스레드보다는 실행자, Task, Stream을 애용하라.

  • 쓰레드는 작업 단위 / 실행 메커니즘을 모두 개발자가 작성해야 함. 
  • 자바는 각종 실행 메커니즘을 추상화한 ExeuctorService를 지원함. 개발자는 이것을 사용하는게 좋음.
  • ExecutorService에는 작업 단위(Runnable, Callable)을 제출해서 사용하도록 하자. 개발자는 작업 단위(Runnable, Callable)만 선언하면 됨. 
  • 서버의 사용 환경에 맞게 적절한 ExecutorService를 사용하는 것이 중요
    • 소규모 서버 → CachedThreadPool
    • 대규모 서버 → FixedThreadPool

 


자바의 Thread는 작업 단위 + 수행 메커니즘을 모두 표현함. 

public static void main(String[] args) {
    // Task 정의
    Thread thread = new Thread(() -> System.out.println("hello"));

    // 실행 메커니즘 정의.
    thread.start();
}

자바의 Thread는 작업 단위와 실행 메커니즘을 모두 표현한다. 위의 코드에서 살펴보면 다음과 같이 분리되어서 각각 실행되는 것을 알 수 있다. 

  • new Thread() : 작업해야 할 Task를 정의함.
  • thread.start() : 쓰레드의 실행 메커니즘을 정의함. 

예전에는 쓰레드를 직접 사용하면서 작업 단위와 수행 메커니즘을 직접 관리했어야 했다. 이런 부분은 굉장히 곤혹스러울 수 있다. 그러나 자바에서 ExecutorService가 지원되기 시작했는데, ExecutorService는 실행 메커니즘을 담당한다. 이제 개발자는 Task만 선언하고, 실행 메커니즘은 ExecutorService에 맡기면 된다.

 


자바의 ExeuctorService

ExecutorService는 쓰레드가 수행 해야 할 작업의 실행 메커니즘을 담당하는 녀석이다. 이미 여러 실행 메커니즘을 지원하는 ExecutorService들이 선언되어 있으며, 개발자는 각 ExeuctorService의 특성을 파악하고 어플리케이션에 맞게 가져다 사용하면 된다. 

ExecutorService executorService = Executors.newSingleThreadExecutor();
ExecutorService executorService1 = Executors.newCachedThreadPool();
ExecutorService executorService2 = Executors.newFixedThreadPool(10);

예를 들어 cachedThreadPool은 소규모 어플리케이션에 적합하다. CachedThreadPool은 새로운 작업 요청이 들어왔을 때, 처리할 쓰레드가 없으면 새로운 쓰레드를 생성한다. 작업이 밀려들어오고, 처리되지 못한 작업들이 많아서 CPU가 100%를 사용하기 시작하면 작업 요청이 올 때 마다 쓰레드를 생성하면서 점점 처리할 수 없게 된다. 만약에 많은 요청이 오는 서버에서 사용한다면 FixedThreadPool을 사용하는 것이 좋다. 

이처럼 이미 ExeuctorService는 쓰레드가 수행해야 할 실행 메커니즘을 이미 잘 구현해두었다. 개발자는 이것을 가져다 쓰기만 하면 된다. 


ExeuctorService를 사용하면 Task를 사용하자

쓰레드는 작업 / 실행 메커니즘으로 나누어져 있다. ExecutorService는 실행 메커니즘을 담당하고, 개발자는 작업(Task)만 정의해서 ExecutorService에 제출하면 된다. 여기서 Task는 Runnable / Callable로 나누어진다. 각각은 다음 차이가 있다.

  • Runnable : 리턴 타입이 없고, 예외도 없음.
  • Callable : 리턴 타입이 있고, 예외도 던질 수 있음. 

Callable이라는 작업 단위를 하나 만들어서 ExeuctorService에 제출해서 결과를 받아보는 코드는 아래에서 확인할 수 잇다.

ExecutorService executorService = Executors.newSingleThreadExecutor();

// 작업 단위 정의
Callable<String> stringCallable = () -> {
    System.out.println("hello");
    return "hello";
};

Future<String> submit = executorService.submit(stringCallable);
try {
    String result = submit.get();
} catch (ExecutionException | InterruptedException e) {
    e.printStackTrace();
}

댓글

Designed by JB FACTORY