有一类多线程编程模式是这样的:启动多个线程,相互独立的(无同步)去计算一个结果,当某一个线程得到结果之后,立刻终止所有线程,因为只需要一个结果就够了。
实际应用场景:作为男生,电脑上必须有苍老师的爱情动作片。这种片子必须藏得非常隐蔽,隐蔽到什么程度呢?自己都忘了把它藏哪里了,这可咋办啊?编写多线程程序,针对每一个硬盘分区,启动一个线程,搜索该硬盘分区上的所有文件,找名字中含有“苍老师”的文件。由于这些片子都是集中存放,因此,我只需要找到一个,就无需在继续寻找。例如,某线程在D盘找到了一个苍老师的文件,则此线程立刻终结,同时,处理C盘,E盘的线程也应该立刻终结,因为既然D盘找到了,其他盘搜索就毫无意义了,肯定没有结果。
ExecutorService.invokeAny()就是为此设计的,他接收的参数是一个List,List中的每一个元素必须实现Callable接口。他的功能是依此启动新的线程执行List中的任务,并将第一个得到的结果作为返回值,然后立刻终结所有的线程。其用法如下:
String s = es.invokeAny(list)
具体的代码如下:
public class ThreadLocalTest {
public static void main(String[] args) {
ExecutorService es = Executors.newFixedThreadPool(10);
List<Callable<String>> list = new ArrayList<>();
//针对每一个硬盘分区,定义一个任务,查找名字中包含有“苍老师”的文件
FileSystem fs = FileSystems.getDefault();
for(Path root : fs.getRootDirectories()) {
if(Files.isDirectory(root)) {
list.add(new FileFinder("苍老师", root));
}
}
//使用invokeAny()方法,当任意一个线程找到结果之后,立刻终结所有线程
try {
System.out.println(es.invokeAny(list));
} catch (InterruptedException | ExecutionException e) {
// TODO Auto-generated catch block
System.out.println(e.getMessage());
}
es.shutdown();
}
}
//定义一个文件查找类,实现Callable接口,可作为线程启动
//同时继承自SimpleFileVisitor,目的是成为一个FileVIsitor,
//在Nio 2中新出现的遍历文件夹方法,需要一个FileVIsitor作为访问器
class FileFinder extends SimpleFileVisitor<Path> implements Callable<String>{
private String key_word;
private Path path;
private String result;
public FileFinder(String key_word, Path path) {
super();
this.key_word = key_word;
this.path = path;
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
throws IOException {
if(file.toString().contains(key_word)) {
//若找到了指定的文件,则结束遍历
result = file.toString();
return FileVisitResult.TERMINATE;
}else {
return FileVisitResult.CONTINUE;
}
}
@Override
public FileVisitResult visitFileFailed(Path file, IOException exc)
throws IOException {
// 这个方法必须重载,因为遇到系统文件后,可能会被拒绝访问并抛出异常
// 重载了这个方法后,遇到问题直接跳过,就不会抛出异常了
return FileVisitResult.SKIP_SUBTREE;
}
@Override
public String call() throws Exception {
try {
Files.walkFileTree(path, this);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if(result == null) {
//若没找到,则抛出一个异常
//该异常的作用就是告诉invokeAny()函数,此线程未能找到合适的结果
//invokeAny()函数会很好的处理该异常,这是该函数的正常处理机制
throw new Exception("没找到");
}
return result;
}
}
本文介绍了如何使用Java线程的ExecutorService.invokeAny()方法来实现多线程并行计算。该方法适用于启动多个线程执行独立任务,一旦有一个线程完成任务并获取结果,它会立即停止所有其他线程。文中通过一个趣味性的例子,即搜索硬盘中特定文件的场景,来解释invokeAny()的实用价值。示例代码展示了如何创建任务列表并调用invokeAny()获取首个结果,从而提高程序效率。
426

被折叠的 条评论
为什么被折叠?



