20-案例实现
案例实现
根据如下步骤实现本案例。
1.创建一个名为 FileSearch
的类,并实现 Runnable
接口。该类实现了在文件夹及其子文件夹中搜索指定扩展名并在过去24小时内修改过的文件:
public class FileSearch implements Runnable {
2.声明一个私有的 String
类型的属性,用来存储将要搜索的文件夹路径:
private final String initPath;
3.声明另外一个私有的 String
类型属性,用于存储将要搜索的文件扩展名:
private final String fileExtension;
4.声明一个私有的 List
属性,用于存储满足搜索条件的文件的完整路径:
private List<String> results;
5.声明一个私有的 Phaser
属性,用来对任务的不同阶段进行同步控制:
private Phaser phaser;
6.实现该类的构造器,完成实例属性的初始化。构造器接收初始文件夹路径、文件扩展名以及一个 Phaser
对象作为参数:
public FileSearch(String initPath, String fileExtension,
Phaser phaser) {
this.initPath = initPath;
this.fileExtension = fileExtension;
this.phaser = phaser;
results = new ArrayList<>();
}
7.实现一些在 run()
方法中需要使用的辅助方法。首先是 directoryProcess()
方法,该方法接收一个 File
对象作为参数,并将处理其文件及子文件夹。该方法把每个文件夹作为参数来递归调用。对于每个文件,该方法将会调用 fileProcess()
方法来进行处理:
private void directoryProcess(File file) {
File list[] = file.listFiles();
if (list != null) {
for (int i = 0; i < list.length; i++) {
if (list[i].isDirectory()) {
directoryProcess(list[i]);
} else {
fileProcess(list[i]);
}
}
}
}
8.实现 fileProcess()
方法。该方法接收一个 File
对象作为参数,然后判断它的扩展名是否与指定的扩展名相同,如果相同,则将该文件的完整路径存储到 results
列表中:
private void fileProcess(File file) {
if (file.getName().endsWith(fileExtension)) {
results.add(file.getAbsolutePath());
}
}
9.实现 filterResults()
方法。该方法不需要任何参数,直接对第一阶段的 results
列表进行过滤,该方法将会剔除修改时间大于24小时的文件。首先,创建一个空的列表 newResults
并得到当前时间 actualDate
:
private void filterResults() {
List<String> newResults = new ArrayList<>();
long actualDate = new Date().getTime();
10.遍历 results
列表中的所有元素。对于列表中的每个路径,创建对应的 File
对象,并获取其最后修改时间:
for (int i = 0; i < results.size(); i++) {
File file = new File(results.get(i));
long fileDate = file.lastModified();
11.比较文件的修改时间与当前时间,如果修改时间小于一天,则将文件的完整路径添加到新的结果列表中:
if (actualDate - fileDate < TimeUnit.MILLISECONDS
.convert(1, TimeUnit.DAYS)) {
newResults.add(results.get(i));
}
}
12.将 results
引用指向过滤后的新结果列表:
results=newResults;
}
13.实现 checkResults()
方法。该方法将在第一阶段和第二阶段结束时被调用,它将会检查 results
列表是否为空,不需要任何参数:
private boolean checkResults() {
14.检查 results
列表的大小。如果列表为空,则向控制台输出相关信息,然后调用 Phaser
对象的 arriveAndDeregister()
方法,它表明当前线程已经完成当前阶段任务,并且不再参与后续阶段的工作:
if (results.isEmpty()) {
System.out.printf("%s: Phase %d: 0 results.\n",
Thread.currentThread().getName(),
phaser.getPhase());
System.out.printf("%s: Phase %d: End.\n",
Thread.currentThread().getName(),
phaser.getPhase());
phaser.arriveAndDeregister();
return false;
15.如果 results
列表不为空,则向控制台输出列表大小。然后,调用 Phaser
对象的 arriveAndAdvance()
方法,它表明该线程已经完成当前阶段的任务,并且希望等待其他参与该阶段的线程完成当前阶段任务:
} else {
System.out.printf("%s: Phase %d: %d results.\n",
Thread.currentThread().getName(),
phaser.getPhase(),results.size());
phaser.arriveAndAwaitAdvance();
return true;
}
}
16.最后一个辅助方法为 showInfo()
,用来将最终的结果列表元素输出到控制台中:
private void showInfo() {
for (int i = 0; i < results.size(); i++) {
File file = new File(results.get(i));
System.out.printf("%s: %s\n",
Thread.currentThread().getName(),
file.getAbsolutePath());
}
phaser.arriveAndAwaitAdvance();
}
17.完成所需的辅助方法后,开始实现 run()
方法。并发任务的阶段转变也是通过 Phaser
对象来控制的。在 run()
方法一开始便调用了 Phaser
对象的 arriveAndAwaitAdvance()
方法,这样只有在所有线程创建完毕并开始运行后,才能开始搜索任务:
@Override
public void run() {
phaser.arriveAndAwaitAdvance();
18.向控制台输出当前线程搜索任务开始的信息:
System.out.printf("%s: Starting.\n",
Thread.currentThread().getName());
19.检查 initPath
是否为文件目录,如果是,则调用 directoryProcess()
方法在该文件夹及其子文件夹下搜索指定扩展名的文件:
File file = new File(initPath);
if (file.isDirectory()) {
directoryProcess(file);
}
20.用 checkResults()
方法检查是否搜索到结果。如果没有,则通过 return
关键字结束线程:
if (!checkResults()) {
return;
}
21.过滤第一步得到的结果:
filterResults();
22.再次用 checkResults()
检查是否搜索到结果。如果没有,则通过 return
关键字结束线程:
if (!checkResults()) {
return;
}
23.通过 showInfo()
方法向控制台输出最后的结果,向 Phaser
对象注销当前任务,并输出当前线程结束的信息:
showInfo();
phaser.arriveAndDeregister();
System.out.printf("%s: Work completed.\n",
Thread.currentThread().getName());
24.实现案例的主程序的入口 main()
方法:
public class Main {
public static void main(String[] args) {
25.创建1个拥有3个同步参与者的 Phaser
对象:
Phaser phaser=new Phaser(3);
26.创建3个具有不同初始文件夹的 FileSearch
对象,查找在这些文件夹下拥有 .log
扩展名的文件:
FileSearch system=new FileSearch("C:\\Windows", "log", phaser);
FileSearch apps= new FileSearch("C:\\Program Files",
"log",phaser);
FileSearch documents= new FileSearch("C:\\Documents And Settings",
"log",phaser);
27.创建并启动一个线程执行第一个 FileSearch
对象:
Thread systemThread=new Thread(system,"System");
systemThread.start();
28.创建并启动一个线程执行第二个 FileSearch
对象:
Thread appsThread=new Thread(apps,"Apps");
appsThread.start();
29.创建并启动一个线程执行第三个 FileSearch
对象:
Thread documentsThread=new Thread(documents,"Documents");
documentsThread.start();
30.等待3个线程执行完毕:
try {
systemThread.join();
appsThread.join();
documentsThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
31.调用 Phaser
对象的 isTerminated()
方法获取其是否已经终止的标志,并输出:
System.out.println("Terminated: "+ phaser.isTerminated());