'Exception'에 해당되는 글 1건

  1. 2014.09.03 RejectedExecutionException 해결 방법 알아보기
공부/java2014. 9. 3. 15:39

java.util.concurrent.RejectedExecutionException 을 해결 하는 방법에 대해 예제 코드와 해결 방법에 대하여 이야기 해보겠습니다.

threads 를 만들어서 Executor 인터페이스를 이용하여 실행 하는 경우 실행 할 수 없는 상태가 될 수도 있습니다.

이것은 몇가지 이유가 있을 수 있고 아래의 예제를 통해 보여드리도록 하겠습니다.

그리고 RejectedExecutionExceptionjava.lang.RuntimeException 이라는 것을 주목 할 필요가 있습니다.

1. 간단한 Executor 샘플

RejectedExecutionException 예외를 설명 하기 위해 ThreadPoolExecutor 를 이용하여 몇개의 worker thread 들을 실행하는 간단한 java 어플리케이션을 만들것입니다.

어떻게 만들었는지 아래의 코드를 보겠습니다.

아래의 코드는 ThreadPoolExecutor 클래스가 어떻게 당신이 만든 task 들을 몇개의 thread 들을 만들어 실행하는지 보여줍니다.

프로그램을 실행하면 정상적으로 실행 되는 것을 확인할 수 있습니다.

우리는 ThreadPoolExecutor 를 이용하였는데 pool 의 크기를 3으로 설정 하였습니다.

이것이 의미 하는것은 3개의 thread 들을 만들것이고 3개의 task worker 들을 채워 넣을것입니다.

우리가 새로운 task 를 만들어 ThreadPoolExecutor 에 submit 을 하게 되면 3개의 pool 중 하나가 사용 가능 하게 될때까지 BlockingQueue 에 배치하고 대기 하게 됩니다.

이 예제의 경우 15의 크기를 가지는 ArrayBlockingQueue 를 사용합니다. ArrayBlockingQueue 를 사용한 이유는 나중에 설명 합니다.

예제에서 우리는 10개의 task 들을 만들어 실행 할 것입니다.

2. 간단한 RejectedExecutionException 예제

RejectedExecutionException 의 한가지 이유중에는 새로운 task 를 executor 가 shutdown 된 후에 실행 하려고 할때 발생합니다.

shutdown 메소드가 호출 된 후에는 오래된 task 들은 여전히 실행되지만, 더이상 새로운 task 들을 실행 할 수 없게 됩니다.

이 오류에 대한 내용을 아래의 예제를 통해 확인 해보겠습니다.

프로그램을 실행하게 되면 RejectedExecutionException 이 출력 될 것입니다.

3. RejectedExecutionException 두번째 예제

Executor 가 그의 책임 아래 더이상 task 들을 처리 할 수 없을때 나타나는 예제 입니다.

로컬 메모리의 한계에 도달했을 때 Exception 이 발생합니다.

이 예제에서 로컬 메모리는 ArrayBlockingQueue 의 크기인 15 에 해당합니다.

만약 ArrayBlockingQueue 에 저장 할 수 있는 것보다 더 많은 task 들을 실행 하려고 하면 RejectedExecutionException 이 발생합니다.

예제를 살펴 보겠습니다.

15의 크기를 가지는 ArrayBlockingQueue 는 자신의 크기인 15 보다 더 많은 thread 들을 저장 할 수 없습니다.

하지만 예제에서 우리는 20개의 thread 들을 실행 하려고 하였습니다.

20개의 thread 가 빠르게 처리 된다면 오류가 발생 하지 않을 수도 있지만 우리는 Worker.java 코드에서 Thread.sleep() 으로 딜레이를 주었기 때문에 ThreadPoolExecutor 에서 빠르게 처리 하지 못하고 BlockingQueue 에 저장하게 되는데 이때 15개를 넘어가기 때문에 오류가 발생 한것입니다.

만약 18개의 thread 들을 만들었다면 정상적으로 실행 될 것입니다. 한번 생각해보는것도 좋을꺼 같습니다.

4. RejectedExecutionException 해결 방안

먼저 다음 두가지의 경우를  체크 해봅니다.

  1) shutdown 메소드가 호출 된 후 Executor 가 실행 되는것을 주의 해서 프로그래밍을 한다.

  2) Executor 가 처리 할 수 있는 것보다 더 많은 task 들을 실행 하지 않는다.

두번째의 경우는 쉽게 해결이 가능 합니다. 크기에 제한이 없는 자료구조를 이용 하면 됩니다.

예를 들면 LinkedBlockingQueue 가 있습니다.

만약 LinkedBlockingQueue 를 사용 하였는데도 여전히 문제가 발생한다면 첫번째 케이스를 의심 해봐야 합니다.

만약 첫번째 케이스에도 해당하지 않는다면 분명 여러가지 복합적인 문제가 있을 수 있습니다.

예를 들면 다른 thread 들이 deadlock 에 걸려 메모리가 꽉 차거나 LinkedBlockingQueue 는 제한 없이 계속 쌓이므로 메모리의 사용이 많아져서 문제가 발생 할 수 있습니다.

우리는 두번째 케이스를 좀 더 세밀하게 촛점을 맞춰 해결하려고 합니다.

ThreadPoolExecutor 는 15개 보다 더 많은 task 들을 처리 할 수 있습니다. 

이 예제에서는 ArrayBlockingQueue 를 사용하고 있고 이는 새로운 task 가 실행되기 전에 처리가 완료된다면 자연스럽게 오류가 없이 실행이 될 수 있습니다.

약간의 꼼수 같은 예제 이지만 한번 살펴 보겠습니다.

예제를 실행 해보면 문제없이 실행 되는것을 확인 할 수 있습니다.

예제를 실행해 보면 마치 ThreadPoolExecutor 가 15개 이상의 task 들을 처리 할 수 있는 것 처럼 보이지만 사실 약간의 꼼수를 이용하여 딜레이를 줘 ThreadPoolExecutor 가 이미 처리 중인 task 들이 충분히 처리 되어 queue 를 비울 수 있는 시간을 벌게 해줬기 때문에 가능한것이다. Thread.sleep(3000); 이부분은 꼭 3000 이 아니라 적절한 값을 주면 됩니다.




소스

예제 출처 : 


'공부 > java' 카테고리의 다른 글

Java List 를 배열로 변환 하는 예제  (0) 2014.09.05
Java FileWriter 예제  (0) 2014.09.05
예외  (0) 2014.06.20
POJO  (0) 2014.06.10
자바의 주요 특징 및 개인적인 생각  (0) 2014.06.08
Posted by #HanaLee