当前位置:嗨网首页>书籍在线阅读

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());