원격 위치의 항목을 나열하는 Java 반복기가 있습니다. 항목 목록이 "페이지"로 나오고 "다음 페이지 가져 오기"작업이 다소 느립니다. (필자의 반복자는 S3Find
이고 Amazon S3의 객체를 나열합니다.리소스 누출 위험없이 Java 반복자 내에서 ExecutorService를 사용하는 방법
그래서 일을 빠르게하기 위해 하나의 목록 페이지를 미리 가져오고 싶었습니다. 이렇게하려면 항목의 "페이지"를 미리 가져 오기 위해 ExecutorService
및 Callable
/Future
패턴을 사용했습니다. 문제는 반복기의 호출자가 수업을 알리지 않고 언제든지 작업을 포기할 수 있다는 것입니다. 예를 들어, 다음 루프 고려 :
for (S3URL f : new S3Find(topdir).withRecurse(true)) {
// do something with f
if (some_condition) break;
}
결과를하는 포함하는 더 이상 언급이없는 경우에도 나는 Callable
을 제출하는 데 사용하는 ExecutorService
살아 실행 남아, 자원 누수가 S3Find
(그리고 다음 프리 페치가 완료 되더라도).
이 문제를 해결하는 적절한 방법은 무엇입니까? 잘못된 접근 방식을 사용하고 있습니까? 방금 ExecutorService
을 포기하고 모든 프리 페치에 대해 새로운 베어 스레드를 사용해야합니까? (그리고 프리 페치가 완료되면 스레드를 죽이십시오)? 페이지를 가져올 때마다 대략 500ms가 소요되므로 매번 새로운 스레드를 만드는 것은 아마도 비교할 때 무시할 만하다. 한 가지가 있습니다 은 발신자가 S3Find
에 반복적으로 수행되었음을 알리도록 요구하는 것입니다 (일부는 잊어 버릴 수 있음).
/**
* This class holds one ObjectListing (one "page"), and also pre-fetches
* the next page using a {@link S3Find#NextPageGetter} Callable on a
* separate thread.
*/
private static class Pager {
private final AmazonS3 s3;
private ObjectListing currentList;
private Future<ObjectListing> future;
private final ExecutorService exec;
public Pager(AmazonS3 s3, ListObjectsRequest request) {
this.s3 = s3;
currentList = s3.listObjects(request);
exec = Executors.newSingleThreadExecutor();
future = submitPrefetch();
}
public ObjectListing getCurrentPage() {
return currentList;
}
/**
* Move currentList to the next page, and returns it.
*/
public ObjectListing getNextPage() {
if (future == null) return null;
try {
currentList = future.get();
future = submitPrefetch();
} catch (InterruptedException|ExecutionException e) {
e.printStackTrace();
}
return currentList;
}
private Future<ObjectListing> submitPrefetch() {
if (currentList == null || !currentList.isTruncated()) {
exec.shutdown();
return null;
} else {
NextPageGetter worker = new NextPageGetter(s3, currentList);
return exec.submit(worker);
}
}
}
/**
* This class retrieves the "next page" of a truncated ObjectListing.
* It is meant to be called in a Callable/Future pattern.
*/
private static class NextPageGetter implements Callable<ObjectListing> {
private final ObjectListing currentList;
private final AmazonS3 s3;
public NextPageGetter(AmazonS3 s3, ObjectListing currentList) {
super();
this.s3 = s3;
this.currentList = currentList;
if (currentList == null || !currentList.isTruncated()) {
throw new IllegalArgumentException(currentList==null ?
"null List" : "List is not truncated");
}
}
@Override
public ObjectListing call() throws Exception {
ObjectListing nextList = s3.listNextBatchOfObjects(currentList);
return nextList;
}
}
감사합니다. 사실 S3Find.close()를 피하려고합니다. 모든 사람이 갑자기 명시 적 호출을 사용하도록 요구하는 것은 1) 반복자가 이미 사용 된 곳에서 수백 개의 인스턴스를 수정해야합니다 (예전에 이것을 작성했으며 어제는 미리 가져 오기 속도를 높이는 것을 고려했기 때문에). 2) 어쨌든 잊어 버렸습니다. 일부 사용자는 Javadoc이 얼마나 많은지 상관하지 않습니다. 마지막으로 내가 원하는 것은 리소스 누출에 대한 또 다른 기회를 소개하는 것입니다 ... (그곳에는 많은 것들이 있습니다). –
예, 동의합니다. 일반적으로 창 닫기는 대단합니다. –
예, 이해하신 @ 피에로 드. 불행히도 그 주위에 방법이 없습니다. 행운을 빌어 요. – Gray