我正在尝试通过执行程序服务来教自己使用多线程,并且想知道在下面实现我的代码的最佳实践是什么-我正在读取文本文件目录并检查字符/单词-
如果我所做的只是通过文件列表对每个线程进行操作,我也很困惑是否正在使用多个线程。是否一次处理多个文件?
主班
public class Application {
private long totalCharacterCount;
private long totalLineCount;
private final File[] fileList;
private final static String _DIRECTORY = "src//documents";
public Application(String directory){
fileList = new File(directory).listFiles();
}
public synchronized File[] getFileList(){
return fileList;
}
public static void main(String[] args) throws InterruptedException, ExecutionException {
ExecutorService executor = Executors.newFixedThreadPool(4);
Application x = new Application(_DIRECTORY);
for(File file : x.getFileList()){
Future<FileReadings> response = executor.submit(new Process(file));
x.totalCharacterCount += response.get().characterCount;
x.totalLineCount += response.get().lineCount;
}
System.out.println("Total lines in all documents: " + x.totalLineCount);
System.out.println("Total characters in all documents: " + x.totalCharacterCount);
executor.shutdown();
}
}
进程类
public class Process implements Callable<FileReadings> {
private FileReadings object;
private File file;
public Process(File file){
FileReadings obj = new FileReadings();
this.object = obj;
this.file = file;
}
public void CountCharacters(File file){
int count = 0;
try {
BufferedReader reader = Files.newBufferedReader(file.toPath());
while(reader.read() != -1){
count++;
}
object.characterCount = reader.read();
} catch (IOException ex) {
ex.printStackTrace();
}
object.characterCount = count;
}
public void CountLines(File file){
try {
Stream<String> text = Files.lines(file.toPath());
object.lineCount = text.count();
} catch (IOException ex) {
ex.printStackTrace();
}
}
public FileReadings call() throws Exception {
CountCharacters(this.file);
CountLines(this.file);
System.out.println("THERE WERE: " + object.characterCount + " CHARACTERS IN: " + file.getName());
System.out.println("THERE WERE: " + object.lineCount + " LINES IN: " + file.getName());
return object;
}
}
文件阅读类
public class FileReadings {
long characterCount;
long lineCount;
}
结果:
THERE WERE: 65 CHARACTERS IN: test1.txt
THERE WERE: 10 LINES IN: test1.txt
THERE WERE: 88 CHARACTERS IN: test2.txt
THERE WERE: 21 LINES IN: test2.txt
Total lines in all documents: 31
Total characters in all documents: 153
答案 0 :(得分:1)
不。这不是正确的方法。您提交一个进程,然后在将来调用get()
,则阻塞并等待其完成,因此实际上这是一个同步处理。
有两种方法可以并行执行异步处理:
1)invokeAll()
这是一种更简单的方法,但是它要求您预先创建所有流程实例,因此它取决于要执行的并行任务数(如果有数百万个并行任务,则可能会达到内存限制)。一旦创建了流程,就可以立即将其提交给执行者。它将并行执行所有任务(根据线程池大小),并在所有任务完成后返回。
List<Callable<FileReadings>> tasks = new Arraylist<>();
for (File file : x.getFileList()) {
tasks.add(new Process(file));
}
// submit all processes at once. they will be processed in parallel
// this call blocks until all tasks are finished
List<Future<FileReadings>> responses = executor.invokeAll(tasks);
// at this point all processes finished. all get() will return immediately
for (Future<FileReadings> response : responses) {
x.totalCharacterCount += response.get().characterCount;
x.totalLineCount += response.get().lineCount;
}
2)submit()
此解决方案在您创建进程并立即提交时具有更高的可伸缩性,因此内存需求是恒定的(不包括执行程序)。但是,您需要自己管理响应:
List<Future<FileReadings>> responses = new ArrayList<>();
for (File file : x.getFileList()) {
responses.add(executor.submit(new Process(file)));
}
// at this point all processes submitted but not finished.
// need to check which is finished at intervarls
while (responses.isEmpty() == false) {
Thread.sleep(1000); // allow some processing time for tasks
// ListIterator allows removing items
ListIterator<Future<FileReadings>> itr = responses.listIterator();
while (itr.hasNext()) {
Future<FileReadings> response = itr.next();
// if task is complete, get it and remove from list
if (response.isDone()) {
x.totalCharacterCount += response.get().characterCount;
x.totalLineCount += response.get().lineCount;
itr.remove();
}
}
}