繁体   English   中英

如何使用 FileVisitor 接口以多线程方式使用 Java Files.walkFileTree()?

[英]How can Java Files.walkFileTree() be used in a multiThread fashion using the FileVisitor interface?

需要扫描整个计算机的文件,我想在计算机上的每个物理驱动器上启动 Files.walkFileTree(startingFolder, FileVisitor)。 但是,我发现 FileVisitor 覆盖方法越来越混乱。 如果我只启动一个线程,一切正常,但是当我启动第二个或更多线程时,第一个线程的 FileVisitor 方法将被忽略或使用不正确的文件进行调用。 Files.walkFileTree() 是一个 static 方法。 我为每个线程指定了一个新的 FileVisitor 实现,但无济于事。 我发现 Files.walkFileTree() 比其他方法快大约 65%。 我怎样才能使这个多线程?

节日快乐,谢谢。

彼得杰科维茨

示例代码:

ArrayList<Path> seeds = new ArrayList<>();   // This is where we are going to start looking 


private void launchTreeWalkers() {
  
 ArrayList<Path> effectivelyFinalPath = this.seeds;   // Physical drives on the computer ie  C:\ D:\ E:\ ...   
  int[] index = new int[1];       //  Effectively Final
  this.twThreads = new Thread[ effectivelyFinalPath.size() ];  // TreeWalkerThreads 
  
  for ( int i=0; i < effectivelyFinalPath.size(); i++ ) {
    index[i] = i; 
    
    this.twThreads[i] = new Thread( new Runnable() { 
                             @Override
                             public void run() {
                               try {
                                tTreeWalker2( effectivelyFinalPath.get( index[0]) );
                              } catch ( Exception ex ) {
                                ex.printStackTrace();
                              }
                             }
                           }
                         );
    
    this.twThreads[i].setDaemon( true );  // So the thread will die when the parent dies.
    this.twThreads[i].setName("Tree Walker");
    this.twThreads[i].start();
  }  
  
  Display.fmt("%1$,d Tree Walker thread%2$s %3$s been started.", box( this.twThreads.length ), Aide.plural(this.twThreads.length), Aide.hasHave( this.twThreads.length ) ); 
}

/** A walkFileTree thread   */
void tTreeWalker( Path startingPath ) throws Exception {
    
  @SuppressWarnings("resource")  // java.lang.UnsupportedOperationException when closing FileSystem  
  FileSystem fileSystem = FileSystems.getDefault();   

  Files.walkFileTree( fileSystem.getPath( startingPath.toString() ), new FileVisitorImpl()); 
}


/** */
public class FileVisitorImpl implements FileVisitor<Path> {
  
  @Override   // Called before a directory visit.
  public FileVisitResult preVisitDirectory( Path directory, BasicFileAttributes attrs ) throws IOException {
    Locate.this.folderCountLA.increment();  // Long Adder

    if ( isMatch( excludeFolders, directory ))
      return FileVisitResult.SKIP_SUBTREE;

    Locate.this.foldersSelectedLA.increment(); 
    return FileVisitResult.CONTINUE;
  }

  @Override   // Called after a directory visit is complete.
  public FileVisitResult postVisitDirectory( Path dir, IOException exc ) throws IOException {
    return FileVisitResult.CONTINUE;
  }

  @Override    // This method is called for each file visited. The basic attributes of the files are also available.
  public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
    Locate.this.fileCountLA.increment();

    if ( ! isMatch( excludeFiles, file  ) ) {
      FileOfInterest foi = new FileOfInterest( file );
      
      try { 
        if (( probZips ) && ( foi.isCompressedFile() )) { 
          foundFilesQ.put( foi );
          //TODO  Zip handler goes here 
          
        } else if ( isMatch( selectFiles, file  ) ) {   
          Locate.this.filesSelectedLA.increment();
          foundFilesQ.put( foi );   // Blocking if the Q is full.    
        }

      } catch ( InterruptedException ex ) { // We do not expect this to ever happen.
        ex.printStackTrace();
      }
    }
    return FileVisitResult.CONTINUE;
  }

  @Override   // If the file visit fails for any reason, the visitFileFailed method is called.
  public FileVisitResult visitFileFailed( Path file, IOException exc) throws IOException {

    if ( ! isMatch( knownFailures, file ) ) { // Ignore known failures 
      String msg = exc.toString();

      if ( msg.contains( "java.nio.file.AccessDeniedException" )) {
        Locate.this.accessDeniedLA.increment();

        if ( showAccessDenied ) 
          Display.fmt("visitFileFailed  %s", exc.toString() );
      } else {
        Locate.this.failuresLA.increment();
        Display.fmt("visitFileFailed  %s", exc.toString() );        
      }
    }  

    return FileVisitResult.CONTINUE;
  }
}  // End of FileVisitorImpl   

问题解决了。 试图通过创建 Effectively Final 变量作为 arrays 来愚弄 java 以用于创建 runnable,这让我很苦恼。 当我在我的测试用例中创建 4 个线程时,所有 4 个线程都在同一个驱动器上运行,最后一个进入。 它们没有按照我创建可运行程序的顺序使用。 我通过让线程访问 AtomicInteger 来索引包含驱动器的数组来解决这个问题。 我还在线程中创建了 FileVisitorImpl。 谢谢,新年快乐。 彼得杰科维茨

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM