작업이 들어온 순간 스레드를 생성하는 대신 스레드를 미리 생성하고 싶으면 ThreadPoolExecutor.prestartAllCoreThreads()를 사용하면 기본 스레드를 미리 생성할 수 있다. 참고로 ExecutorService는 이 메서드를 제공하지 않는다.
아무튼 new ThreadPoolExecutor( corePoolSize, maximumPoolSize,keepAliveTime, TimeUnit unit,BlockingQueue workQueue) 이를 잘 활용하여 다양한 Executor 전략을 사용할 수 있다.
newSingleThreadPool()를 사용하는 전략이다. 스레드풀에 1개의 스레드만 있으며, 주로 테스트용으로 쓰인다.
newFixedThreadPool(nThreads)를 사용하는 전략이다. 생성할 스레드 수를 미리 정해놓기 떄문에 사용할 cpu 리소스나 자원을 알 수 있어 예측 가능한 전략이다. 그 덕분에 안정적으로 사용할 수 있다는 장점이 있지만, 이는 단점도 된다. 이벤트를 열어 갑자기 너무 많은 사용자가 몰려오면 고정된 스레드 수 때문에 작업 처리 속도가 느려질 수 있다.
newCachedThreadPool()를 사용하는 전략이다. 초과 스레드를 무제한으로 생성하는 것과 더불어 저장 공간이 0인 SynchronousQueue을 사용하여 작업 수가 갑자기 급증하더라도 다 처리할 수 있다. 다만 이 또한 단점이 될 수 있는데 스레드를 무제한으로 생성하는 만큼 작업 수가 너무 많이 들어오면 cpu와 메모리를 완전히 사용할 수도 있다. 그렇게 되면 서버가 죽어버릴 가능성이 생긴다.
큐에 저장 공간이 없다. 따라서 생산자가 소비자하고 직접 만나 작업을 건네는 직거래 방식이다. 좀 더 구체적으로 말하자면 생산자가 소비자를 기다리고 소비자가 오면 작업을 건네고 그 결과를 반환받는 형식이다.
사용자에 따라 다르게 사용할 수 있다. new ThreadPoolExecutor( corePoolSize, maximumPoolSize,keepAliveTime, TimeUnit unit,BlockingQueue workQueue) 이를 적절히 활용하는 방식이다. 예를 들어 new ThreadPoolExecutor(100, 200, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<>(1000)); 이렇게 정의할 경우 1100개까지는 기본 스레드로 처리하지만 넘어갈 경우 초과 스레드를 생성하여 처리한다. 만약 1200개도 넘어갈 경우 작업이 너무 만하다고 들어오느 작업을 거절해버린다. 이렇게 하면 딱 한계까지 cpu를 사용할 수 있고 작업도 좀 더 빠르게 처리할 수 있다.
작업이 너무 많이 들어와 거절당했을 때 어떻게 할지를 설정할 수 있다.
ExecutorService executor = new ThreadPoolExecutor(1, 1, 0, TimeUnit.SECONDS,new SynchronousQueue<>(), new ThreadPoolExecutor.AbortPolicy());
이런 형식으로 설정할 수 있다. 참고로 AbortPolicy는 기본 정책이므로 생략해도 된다.
사용자 정의인 경우에는 handler를 따로 만들어서 넣어주면 된다.