@Andy x:
Zależy co te taski robią, bo jak je tylko uruchamiasz z jakimś ograniczeniem, a one sobie "uruchamiają CyberPunka każdy jeden"? ;)
Puść sobie prosty przykład (klasycznie liczby Fibonacci) i obserwuj jak pod koniec zadań (blisko 50) zachowuje się obciążenie rdzeni.
Na koniec będą obciążone 3, potem 2 i jeden (najdłużej, liczący fibonacci(49)).
Po prostu, co wrzucisz, co zadasz, to jest robione.
package com.app;
import lombok.SneakyThrows;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.*;
public class Example {
@SneakyThrows
public static void main(String[] args) {
int nThreads = Runtime.getRuntime().availableProcessors();
final ExecutorService executorService = Executors.newFixedThreadPool(nThreads);
List<HardWork> taskList = getTasks(50);
final List<Future<Long>> resultList = executorService.invokeAll(taskList);
System.out.println("Ready");
for (Future<Long> future : resultList) {
final Long fibonacciNumber = future.get();
System.out.println(NumberFormat.getNumberInstance().format(fibonacciNumber));
}
executorService.shutdown();
}
private static List<HardWork> getTasks(int hardestOne) {
final ArrayList<HardWork> tasks = new ArrayList<>();
for (int i = 0; i < hardestOne; i++) {
tasks.add(new HardWork(i));
}
return Collections.unmodifiableList(tasks);
}
private static class HardWork implements Callable<Long> {
private final int n;
public HardWork(int n) {
this.n = n;
}
@Override
public Long call() {
return fibonacci(n);
}
private Long fibonacci(int n) {
if (n < 2) {
return (long) n;
}
return fibonacci(n - 1) + fibonacci(n - 2);
}
}
}
Jak lepiej opracujesz taski, to dostaniesz inne rezultaty. Na przykład Fibonancci nie rekurencyjnie a iteracyjnie w mig policzy aż do przepełnienia Long.
private Long fibonacci(int n) {
Long[] fibonacci = new Long[n + 2];
fibonacci[0] = 0L;
fibonacci[1] = 1L;
for (int i = 2; i <= n; i++) {
fibonacci[i] = fibonacci[i - 1] + fibonacci[i - 2];
}
return fibonacci[n];
}
Jeszcze jedno
final List<Future<Long>> resultList = executorService.invokeAll(taskList);
blokuje wykonanie wątku main dopóki wszystkie Future nie bedą gotowe.
A submit to puszczenie wątku bez blokowania dalszego natychmiastowego wykonania main
Zobacz kiedy pojawi się "Ready" wypisane przez main w pierwszym i drugim przypadku
invoke jest operacją blokującą, puści dalej dopiero gdy wszystkie Future będą gotowe
submit jest operacją nieblokująca, puszcza Thread do executorService i na nic nie czeka
package com.app;
import lombok.SneakyThrows;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.*;
public class Example {
@SneakyThrows
public static void main(String[] args) {
int nThreads = Runtime.getRuntime().availableProcessors();
final ExecutorService executorService = Executors.newFixedThreadPool(nThreads);
List<HardWork> taskList = getTasks(44);
for (HardWork hardWork : taskList) {
executorService.submit(hardWork);
}
System.out.println("Ready");
executorService.shutdown();
}
private static List<HardWork> getTasks(int hardestOne) {
final ArrayList<HardWork> tasks = new ArrayList<>();
for (int i = 0; i < hardestOne; i++) {
tasks.add(new HardWork(i));
}
return Collections.unmodifiableList(tasks);
}
private static class HardWork implements Callable<Void> {
private final int n;
public HardWork(int n) {
this.n = n;
}
@Override
public Void call() {
System.out.println(NumberFormat.getNumberInstance().format(fibonacci(n)));
return null;
}
private Long fibonacci(int n) {
if (n < 2) {
return (long) n;
}
return fibonacci(n - 1) + fibonacci(n - 2);
}
}
}
Przeanalizuj sobie te dwa przykłady, powinny ci pomóc trochę to zrozumieć