在 java 中使用按钮停止或退出方法有困难

Having difficulties stopping or exiting a method using a button in java

我正在开发一个搜索重复文件的 GUI 程序。我目前面临的问题是,当单击搜索按钮时,它会继续搜索直到完成。但是,我希望用户可以通过停止重复搜索方法来取消或停止搜索。我尝试在重复搜索方法中使用 return 语句,但它不起作用。这是我的代码:

public void findDuplicateFiles(File[] files) throws IOException  {
    Map<String, List<File>> filesByHash = new HashMap<>();
    if(stop.getText() == "stop")
        return;

    for (File file : files) {
        if (!file.isFile()) {
            findDuplicateFiles(file.listFiles());
            continue;  
        }
        String hash = MD5.asHex(MD5.getHash(file));

        List<File> filesForHash = filesByHash.get(hash);
        if (filesForHash == null) { 
            filesByHash.put(hash, filesForHash = new ArrayList<>());
        }
        filesForHash.add(file);
    }
    for (Map.Entry<String, List<File>> entry : filesByHash.entrySet()) {
        List<File> filesForHash = entry.getValue();
        if (filesForHash.size() > 1) {
            String hash = entry.getKey();
            System.out.printf("%,d files have hash %s:%n", filesForHash.size(), hash);
            int index = filesForHash.size() - 1;
            filesForHash.remove(index);
            final DefaultListModel model = (DefaultListModel) list.getModel();

            for (final File file : filesForHash) {
                EventQueue.invokeLater(new Runnable() {
                    public void run() {
                    pathf.setText("Searching Files...." + file.getPath());
                        try {
                            System.getProperty("com.twmacinta.util.MD5.NO_NATIVE_LIB.MD5.dll");
                            MD5.initNativeLibrary();
                            model.addElement(file.getCanonicalFile());
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                });
                System.out.println("  " + file.getCanonicalPath());
            }
        }
    }
}

感谢您的帮助..

我假设当 stop.getText() 为 "stop" 时您想要结束该方法。 首先,'=='比较的是引用,而不是对象,所以它不起作用,你必须使用等号。以防万一,将其用作 "stop".equalsIgnoreCase(stop.getText())。这种方式不区分大小写(可能是 getText returns "Stop")并且防止空值(在这种情况下 getText returns null)

其次,如果在检查文件夹开始时 "stop",它会 return,但它会继续检查文件夹的其余部分在。 为了澄清,假设你有文件夹A,文件夹A/B和A下的很多文件。方法刚刚输入第一个for并且stop.getText()更改为'stop'。 为此,您找到文件夹 B 并进行第二次调用,因为停止已经激活,return 立即,但第一次调用将继续处理文件夹 A

中的其余文件

实际上,您 "plan" 通过使用 EventQueue.invokeLater 将新线程添加到调用队列来处理所有文件。在编写代码时,您没有对您创建的 运行nables 保持任何引用,即使可能,保留所有它们并取消它们也不是一个好主意,因为您无法真正预测它们的状态和全部.

然后,因为它们都是计划好的,所以它们都运行无法阻止它们,因为您的主线程不再直接引用它们。

解决问题的最佳方法可能看起来有点复杂,但应该也能帮助您更好地理解多线程:创建一个新的 class 来实现 Runnable,但也提供 "stop" 方法(随便命名)。这个新的 class 将包含类似于您发布的代码的内容,但不会使用 invokeLater 或类似的东西,它只会直接执行整个过程,并以尽可能细的粒度添加检查。

这是它的样子:

public class DuplicateDetector implements Runnable{

private boolean canContinue = true;

public void run(){
    // prepare the file list and do the stuff you do not want to
    // run on the main thread
    this.startDetection();
}

private void startDetection(){
    for(File f : this.allYourFiles){

      // HERE IS THE KEY TO STOP YOU THREAD
      // Of course, this will only be checked once per file so
      // it will wait until the current file is finished before stopping
      // the execution
      if(this.canContinue){
         ...
      else{
        break;
      }
    }
    // detection finished, you can know if it ended naturally or because
    // of the cancel button with the value of this.canContinue
    // and provide the feedback you want
}

public synchronized void stop(){
  this.canContinue = false;
}

// Other possible functions to prepare the file list and stuff
}

并且在管理 UI 的同一线程中,您将保留对 Runnable class 的引用,以便您可以在单击事件处理程序中的某处使用它的停止功能你的按钮。

我正在考虑类似的事情(你也可以使用 EventQueue.invokeLater):

DuplicateDetector dupDet = new DuplicateDetector();
new Thread(dupDet).start();

在按钮的点击处理程序中,您可以使用

dupDet.stop()

安全,因为它是同步的。然后 DuplicationDetector 将在下次检查 canContinue 的值时正常停止。

还有一些黑暗的方法可以杀死线程,但它们几乎都一个接一个地被弃用。 interrupt 是正确的方法,但它不允许您的线程在取消后提供反馈,例如处理了多少文件、找到重复项等。

IMO 的一个好方法是在执行过程中提供反馈,这对用户总是有好处的。它可以像从主线程定期访问的 DuplicationDetector 中的一个 getProgression public 方法一样简单,让您的想象力尽情发挥。

希望对您有所帮助!