Executor 프레임워크는 작업의 정의 부분과 실행 부분을 서로 분리시켜 준다. 실행 정책을 정하거나 변경하는데 있어 어느 정도 유연성을 갖고 있긴 하지만, 특정 형태의 실행 정책에서는 실행할 수 없는 작업이 있기도 하다.
스레드 풀은 동일하고 서로 독립적인 다수의 작업을 실행할 때 가장 효과적이다. 일반적인 네트워크 기반의 서버 애플리케이션은 이 조건을 대부분 만족한다.
단일 스레드로 동작하는 Executors.newSingleThreadExecutor()
에서 다른 작업을 큐에 등록하고 해당 작업이 실행된 결과를 가져다 사용하는 작업을 실행하면 데드락이 제대로 걸린다.
public class ThreadDeadlock {
ExecutorService exec = Executors.newSingleThreadExecutor();
public class LoadFileTask implements Callable<String> {
private final String fileName;
public LoadFileTask(String fileName) {
this.fileName = fileName;
}
public String call() throws Exception {
// Here's where we would actually read the file
return "";
}
}
public class RenderPageTask implements Callable<String> {
public String call() throws Exception {
Future<String> header, footer;
header = exec.submit(new LoadFileTask("header.html"));
footer = exec.submit(new LoadFileTask("footer.html"));
String page = renderBody();
// Will deadlock -- task waiting for result of subtask
return header.get() + page + footer.get();
}
private String renderBody() {
// Here's where we would actually render the page
return "";
}
}
}
스레드 풀에서 필요로 하는 자원이 제한되어 사용자가 원하는 크기보다 작은 수준에서 동작하는 경우도 있다.
데드락이 발생하지 않더라도 특정 작업이 예상보다 긴 시간동안 종료되지 않고 실행된다면 스레드 풀의 응답 속도에 문제점이 생긴다.
제한 없이 계속해서 대기하는 기능 대신 일정 시간 동안 만 대기하는 메서드를 사용할 수 있다면, 오래 실행되는 작업이 주는 악영향을 줄일 수 있다. 대부분의 블로킹 메서드는 시간이 제한되지 않은 것과 시간이 제한된 것이 함께 만들어져 있다.
- Thread.join()