2016-03-17 3 views
3

У меня есть два вопроса: 1. Что является простейшей канонической формой для запуска Callable в качестве задачи на Java 8, захвата и обработки результата? 2. В приведенном ниже примере, какой самый лучший/самый простой/ясный способ провести основной процесс до тех пор, пока все задачи не будут завершены?Java 8 Concurrency Простая каноническая форма для основной задачи

Вот пример, который у меня есть до сих пор - это лучший подход в Java 8 или есть что-то более основное?

import java.util.*; 
import java.util.concurrent.*; 
import java.util.function.*; 

public class SimpleTask implements Supplier<String> { 
    private SplittableRandom rand = new SplittableRandom(); 
    final int id; 
    SimpleTask(int id) { this.id = id; } 
    @Override 
    public String get() { 
    try { 
     TimeUnit.MILLISECONDS.sleep(rand.nextInt(50, 300)); 
    } catch(InterruptedException e) { 
     System.err.println("Interrupted"); 
    } 
    return "Completed " + id + " on " + 
     Thread.currentThread().getName(); 
    } 
    public static void main(String[] args) throws Exception { 
    for(int i = 0; i < 10; i++) 
     CompletableFuture.supplyAsync(new SimpleTask(i)) 
     .thenAccept(System.out::println); 
    System.in.read(); // Or else program ends too soon 
    } 
} 

Есть ли более простой и понятный способ Java-8 для этого? И как я могу устранить System.in.read() в пользу лучшего подхода?

ответ

5

канонический способ ждать завершения многократных CompletableFuture экземпляра, чтобы создать новый, в зависимости от их всех через CompletableFuture.allOf. Вы можете использовать это новое будущее, чтобы ждать его завершения или запланировать новые действия последующих так же, как с любым другим CompletableFuture:

CompletableFuture.allOf(
    IntStream.range(0,10).mapToObj(SimpleTask::new) 
      .map(s -> CompletableFuture.supplyAsync(s).thenAccept(System.out::println)) 
      .toArray(CompletableFuture<?>[]::new) 
).join(); 

Конечно, это всегда становится проще, если воздержится назначая уникальный идентификатор для каждой задачи. Так как ваш первый вопрос был о Callable, я покажу вам, как вы можете легко представить несколько аналогичных задачи в Callable с помощью ExecutorService:

ExecutorService pool = Executors.newCachedThreadPool(); 
pool.invokeAll(Collections.nCopies(10,() -> { 
    LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(
      ThreadLocalRandom.current().nextInt(50, 300))); 
    final String s = "Completed on "+Thread.currentThread().getName(); 
    System.out.println(s); 
    return s; 
})); 
pool.shutdown(); 

Служба исполнителя возвращаемого Executors.newCachedThreadPool() является неразделенной и не останусь в живых, даже если вы забудете вызывать shutDown(), но это может занять до одной минуты, прежде чем все потоки будут завершены.

С вашего первого вопроса буквально было: «Что такое простейшая каноническая форма для запуска Callable as a task в Java 8, захвата и обработки результата? ", ответ может заключаться в том, что простейшая форма все еще вызывает метод call() напрямую, например.

Callable<String> c =() -> { 
    LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(
      ThreadLocalRandom.current().nextInt(50, 300))); 
    return "Completed on "+Thread.currentThread().getName(); 
}; 
String result = c.call(); 
System.out.println(result); 

Там нет простого способа ...

4

Рассмотрите возможность сбора фьючерсов в список. Затем вы можете использовать join() на каждом будущем ждать их завершения в текущем потоке:

List<CompletableFuture<Void>> futures = IntStream.range(0,10) 
     .mapToObj(id -> supplyAsync(new SimpleTask(id)).thenAccept(System.out::println)) 
     .collect(toList()); 

futures.forEach(CompletableFuture::join); 
Смежные вопросы