Cześć, mam program, w którym pobieram listę plików z folderu i w pętli dla każdego pliku wywołuję pewną aplikację z parametrem - właśnie tym plikiem, aplikacja zwraca mi wynik i zapisuję to do bazy. Jednak takie jedno wywołanie trwa kilka sekund i przy wielu plikach będzie to trwało bardzo bardzo długo. Czy da radę to podzielić na kilka wątków, żeby trwało to szybciej? Jak podzielić tablicę Stringów ze ścieżkami na każdy wątek w taki sposób, żeby inny wątek nie korzystał już z tego elementu tablicy?
Pokaż kod klasy, którego używasz do pobierania tych plikówi jak wygląda ta tablica Stringów.
Ja bym to zrobił tak: użył DirectoryStream
(chyba, że podkatalogi również, wtedy walkFileTree
) i z gotową ArrayList<Path/File>
wywoływał wątki asynchronicznie albo w póli wątków przez ExecutorService
Listę plików pobrałem tak do ArrayList - z tablicy na ArrayListę przeszedłem bo myślałem o usuwaniu
package pl.starits;
import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
public class Main {
public static ArrayList<File> listOfFiles;
public static void main(String[] args) {
Map<String, String> imagesPlates = new HashMap<String, String>();
Main obj = new Main();
String alprDirectory = "openalpr_64";
String imagesDirectory = "images";
String alprCommand = alprDirectory + "/alpr";
File folder = new File(imagesDirectory);
File[] listOfFilesArray = folder.listFiles();
listOfFiles = new ArrayList<File>(Arrays.asList(listOfFilesArray));
for (int i = 0; i < 10; i++) {
if (listOfFiles.get(i).isFile()) {
String image = listOfFiles.get(i).getAbsolutePath();
String command = alprCommand + " " + image;
String plateNumber = obj.executeCommand(command);
if (!plateNumber.isEmpty()) {
imagesPlates.put(plateNumber, image);
}
}
}
for (Map.Entry<String, String> entry : imagesPlates.entrySet()) {
System.out.println(entry.getKey() + " " + entry.getValue());
}
}
private String executeCommand(String command) {
boolean flag = true;
StringBuffer output = new StringBuffer();
Process p;
try {
p = Runtime.getRuntime().exec(command);
p.waitFor();
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = "";
while ((line = reader.readLine()) != null && flag) {
if (line.contains("confidence")) {
line = line.trim();
line = line.replaceAll("\\s+", "");
line = line.substring(1, line.indexOf("confidence"));
output.append(line);
flag = false;
}
}
} catch (Exception e) {
e.printStackTrace();
}
return output.toString();
}
}
Polecałbym użyć puli watków ForkJoin framework albo tak jak kolega wspomniał wcześniej - ExecutorService/ExecutorThreadPool. Czyli musisz zrobić klasę, która będzie odpowiadać zadaniu, które chcesz wykonać i pozbierać wyniki
http://tutorials.jenkov.com/java-util-concurrent/java-fork-and-join-forkjoinpool.html
https://www.javacodegeeks.com/2013/01/java-thread-pool-example-using-executors-and-threadpoolexecutor.html
Bardzo na skróty:
public class CF {
public static void main(String[] args) throws IOException {
Files.list(Paths.get("."))
.map(f-> CompletableFuture.supplyAsync(()->magia(f)).thenAcceptAsync(i->zapiszDoBazy(i)))
.forEach(f ->f.thenAcceptAsync(x-> System.out.println(".")));
}
private static void zapiszDoBazy(Integer i) {
//....
}
private static Integer magia(Path f) {
return 0;
}
}
Masz listę plików i dla każdego tworzysz CompletableFuture
, który będzie robił magię i zapisywał wynik do bazy. Na koniec wykropkowujesz sobie zakończone zadania.