java.util.concurrent.futuretask를 사용하는 좋은 방법입니까?
-
22-08-2019 - |
문제
우선, 나는 API java.util.concurrent를 처음 접한다고 말해야합니다.
나는 무엇을하고 싶습니까?
기본적으로 2 개의 개별 처리를 실행하는 Java 응용 프로그램이 있습니다 ( MyFirstprocess, mysecondprocess), 그러나 이러한 처리는 동시에 실행해야합니다.
그래서 나는 그렇게하려고 노력했습니다.
public void startMyApplication() {
ExecutorService executor = Executors.newFixedThreadPool(2);
FutureTask<Object> futureOne = new FutureTask<Object>(myFirstProcess);
FutureTask<Object> futureTwo = new FutureTask<Object>(mySecondProcess);
executor.execute(futureOne);
executor.execute(futureTwo);
while (!(futureOne.isDone() && futureTwo.isDone())) {
try {
// I wait until both processes are finished.
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
logger.info("Processing finished");
executor.shutdown();
// Do some processing on results
...
}
MyFirstprocess 그리고 mysecondprocess 구현하는 수업입니다 Callable<Object>
, 및 모든 처리가 Call () 메소드에서 이루어지는 경우.
그것은 잘 작동하지만 그것이 올바른 방법인지 확실하지 않습니다. 내가 원하는 것을하는 좋은 방법입니까? 그렇지 않다면, 내 코드를 향상시키기위한 힌트를 줄 수 있습니다.
해결책
당신은 사용하는 것이 더 좋을 것입니다 get()
방법.
futureOne.get();
futureTwo.get();
두 가지 모두 처리를 마친 스레드에서 알림을 기다리므로 현재 사용하고있는 바쁜 시간을 절약 할 수 있습니다. 효율적이거나 우아하지 않습니다.
보너스로 API가 있습니다 get(long timeout, TimeUnit unit)
이를 통해 스레드가 잠을 자고 응답을 기다릴 때까지 최대 시간을 정의하고 계속 실행됩니다.
참조 Java API 더 많은 정보를 위해서.
다른 팁
의 사용 FutureTask
위는 견딜 수 있지만 관용적이지는 않습니다. 당신은 실제로 감싸고 있습니다 추가의 FutureTask
당신이 제출 한 것 주위 ExecutorService
. 당신의 FutureTask
a로 취급됩니다 Runnable
에 의해 ExecutorService
. 내부적으로, 그것은 당신을 감싸고 있습니다 FutureTask
-처럼-Runnable
새로운 FutureTask
그리고 그것을 당신에게 반환합니다 Future<?>
.
대신, 당신은 당신의 제출해야합니다 Callable<Object>
인스턴스 a CompletionService
. 당신은 두 가지를 떨어 뜨립니다 Callable
비어있는 s submit(Callable<V>)
, 그런 다음 돌아 서서 전화하십시오 CompletionService#take()
두 번 (제출 된 각각에 대해 한 번 Callable
). 해당 통화는 하나가 될 때까지 차단되고 다른 제출 된 작업이 완료됩니다.
당신이 이미 가지고 있다는 것을 감안할 때 Executor
손에 새로 구성하십시오 ExecutorCompletionService
그 주위에 당신의 작업을 떨어 뜨립니다. 회전하고 잠들지 마십시오. CompletionService#take()
작업 중 하나가 완료되거나 (실행 또는 취소 된) 또는 대기 대기 스레드가 될 때까지 차단됩니다. take()
중단됩니다.
Yuval의 해결책은 괜찮습니다. 대안으로 다음을 수행 할 수도 있습니다.
ExecutorService executor = Executors.newFixedThreadPool();
FutureTask<Object> futureOne = new FutureTask<Object>(myFirstProcess);
FutureTask<Object> futureTwo = new FutureTask<Object>(mySecondProcess);
executor.execute(futureOne);
executor.execute(futureTwo);
executor.shutdown();
try {
executor.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
} catch (InterruptedException e) {
// interrupted
}
이 접근법의 장점은 무엇입니까? 이 방법을 제외하고는 실제로 많은 차이가 없다. 나는이 관용구를 그 일보다 선호하는 경향이있다.
또한 get ()가 예외를 던지면 두 작업이 수행되었다고 가정하는 코드의 일부에서 끝날 수 있습니다.
invokeall (colelction ....) 메소드를 사용할 수 있습니다
package concurrent.threadPool;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class InvokeAll {
public static void main(String[] args) throws Exception {
ExecutorService service = Executors.newFixedThreadPool(5);
List<Future<java.lang.String>> futureList = service.invokeAll(Arrays.asList(new Task1<String>(),new Task2<String>()));
System.out.println(futureList.get(1).get());
System.out.println(futureList.get(0).get());
}
private static class Task1<String> implements Callable<String>{
@Override
public String call() throws Exception {
Thread.sleep(1000 * 10);
return (String) "1000 * 5";
}
}
private static class Task2<String> implements Callable<String>{
@Override
public String call() throws Exception {
Thread.sleep(1000 * 2);
int i=3;
if(i==3)
throw new RuntimeException("Its Wrong");
return (String) "1000 * 2";
}
}
}
사용하고 싶을 수도 있습니다 사이클리 배저 스레드를 동시에 시작하는 데 관심이 있거나 완료되기를 기다린 다음 추가 처리를 수행합니다. 자세한 내용은 Javadoc을 참조하십시오.
FutureTask가 2보다 2 인 경우 고려하십시오. [ListenableFuture][1]
.
다른 작업이 시작 되 자마자 여러 작업이 시작될 때 - "팬 아웃" - ListenableFuture는 작동합니다 : 요청 된 모든 콜백을 트리거합니다. 약간 더 많은 작업을 통해 우리는 할 수 있습니다."팬인, "또는 다른 여러 미래가 모두 끝나 자마자 청취 가능한 문제가 계산되도록 트리거됩니다.