У меня есть функция, которая последовательно (однопоточно-ly) выполняет итерацию через каталог файлов, изменяя все отступы от вкладок до трехмерного отступа.
Я использую его в качестве первой попытки многопоточности. (Я больше всего использую Java Concurrency на практике... удивил его восемь лет.)
Чтобы сохранить текущую однопоточную функциональность, но добавьте дополнительную возможность многопоточности, я думаю об изменении функции, чтобы принять дополнительный параметр Executor, где исходная однопоточная функция теперь будет вызовом он, проходящий в одном поточном исполнителе.
Это подходящий способ сделать это?
Один из способов заключается в том, что @Victor Сорокин предлагает в своем ответе: обернуть обработку каждого файла в Runnable
а затем либо отправить в Executor
либо просто вызвать run()
из основного потока.
Другая возможность состоит в том, чтобы всегда делать одно и то же обертывание в Runnable
и передавать его всегда предоставленному Executor
.
Независимо от того, выполняется ли обработка каждого файла одновременно или нет, будет зависеть от данной реализации Executor
.
Для параллельной обработки вы можете ссылаться на свою функцию, передавая ее, т.е. ThreadPoolExecutor
в качестве аргумента, тогда как для последовательной обработки вы можете передать поддельный Executor
, то есть тот, который выполняет предоставленные задачи в потоке вызывающего:
public class FakeExecutor implements Executor {
@Override
public void execute(Runnable task) {
task.run();
}
}
Я считаю, что это самый гибкий подход.
Если вы используете Java 8, я обнаружил, что parallelStream
- это самый простой способ реализовать многопоточность
List<File> files = Arrays.asList(getDirectoryContents());
files.parallelStream().forEach( file -> processFile(file));
Если вы хотите иметь возможность изменять однопоточную и многопоточную, вы можете просто передать логический флаг
List<File> files = Arrays.asList(getDirectoryContents());
if(multithreaded){
files.parallelStream().forEach( file -> processFile(file));
}else{
files.stream().forEach(file -> processFile(file));
}
Я хотел бы помочь с Java 7, но я перешел с Java 5 на 8 в одночасье. :) Java 8 стоит sooooooo.
Один из способов создания класса реализует интерфейс Executor, который будет выполнять ваш код в основном потоке. Как это:
public class FileProcessor implements Runnable {
private final File file;
public FileProcessor(File file) {
this.file = file;
}
@Override
public void run() {
// do something with file
}
}
public class DirectoryManager {
private final Executor executor;
public DirectoryManager() {
executor = new Executor() {
@Override
public void execute(Runnable command) {
command.run();
}
};
}
public DirectoryManager(int numberOfThreads) {
executor = Executors.newFixedThreadPool(numberOfThreads);
}
public void process(List<File> files) {
for (File file : files) {
executor.execute(new FileProcessor(file));
}
}
}
и назовите его в своем коде, как это
DirectoryManager directoryManager = new DirectoryManager();
directoryManager.process(lists);
// some other sync code
или это
DirectoryManager directoryManager = new DirectoryManager(5);
directoryManager.process(lists);
// some other async code
Самый простой способ:
Runnable\Callable
(либо анонимный класс, либо явный класс, который implements Runnable\Callable
; Таким образом, вы сможете либо вызвать свой Runnable
в основном потоке (однопоточной версии), либо передать его в Executor
(многопоточная версия).