如何终止检测新文件的连续线程

How to terminate continuous thread that detects new file

这是我第一次涉足 WatchService 和多线程。

我有一个应用程序需要检测外部设备何时发出提示让用户做某事。提示通过放入特定文件夹的 XML 文件传递​​。该文件的标题始终为 "PRM.xml",并且将覆盖以前的 PRM 文件(如果存在)。为了防止 re-displaying 旧提示,我的代码会在显示后删除 PRM 文件。

该应用程序是 user-interactive,因此当用户在主线程上执行其他活动时,它始终在后台线程中侦听 PRM.xml。

问题是当用户想在主线程中结束他的session时(通过输入sentinel "zzz"),监听线程不会结束,应用程序只有在正在监视的文件夹中发生另一个事件。

如何在用户指示主线程终止时强制后台监听线程退出? (我希望我包含了足够的代码以获得良好的响应。)

// Method 'run' contains code to be executed in the thread
public void run() {

  try {
     // initiate new watch service to watch for new prompts
     WatchService ws = dirToWatch.getFileSystem().newWatchService();
     dirToWatch.register(ws, ENTRY_CREATE);

     // monitor directory continuously until main program thread ends
     while (!SmartTill.stCom.equals("ZZZ")) {

        // get new directory events
        WatchKey wk = ws.take();

        // loop through all retrieved events
        for (WatchEvent<?> event : wk.pollEvents()) {

           if (event.context().toString().endsWith(fileToDetect)) {
              System.out.println("DEBUG: Display Prompt, Delete PRM.");
              // ...call to "displayPrompt" method goes here...
              delFile(Paths.get(dirToWatch + fileToDetect));
           }// end if
        }// end for

     // reset the key (erase list of events)
     System.out.println("Key has been " + 
             (wk.reset() ? "reset." : "unregistered."));
     }// end while
  }// end try
  catch(Exception e){}
}// end run

尝试在主线程中添加关闭钩子,这样当主应用程序退出时,您也可以正确终止监听线程。

待完成的更改

根据您的代码,需要进行以下更改:

  1. 允许从主线程关闭 WatchService
  2. 向 Shutdown Hook 添加一个新线程,以便它在应用程序关闭时关闭 WatchService。

1。使 WatchService 可关闭

应该允许在实现监听线程的class中关闭WatchService。

public void stopThread() {
    try {
        System.out.println("closing the ws");
        ws.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

2。在应用程序关闭时关闭 WatchService

在主线程中,添加一个关闭钩子,这样当应用程序关闭时,它会调用监听线程的stopThread()方法class。

Runtime.getRuntime().addShutdownHook(new Thread(new Runnable(listenThreadObj) {
    public void run() {
        listenThreadObj.stopThread();
    }
}));

示例代码

聆听线程

import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;

public class ListeningThread implements Runnable {

    WatchService ws;

    public ListeningThread() {
        try {
            ws = FileSystems.getDefault().newWatchService();
            Path watchPath = FileSystems.getDefault().getPath("<path_to_directory>");
            watchPath.register(ws, StandardWatchEventKinds.ENTRY_CREATE);
            watchPath.register(ws, StandardWatchEventKinds.ENTRY_MODIFY);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run() {
        WatchKey wk;
        try {
            while ((wk = ws.take()) != null) {
                for (WatchEvent<?> we : wk.pollEvents()) {
                    System.out.println(we.kind());
                }
            }
        } catch (InterruptedException e) {
            System.out.println("WatchService closed");
            e.printStackTrace();
        }
    }

    public void stopThread() {
        try {
            System.out.println("closing the ws");
            ws.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

主线程

import java.util.Scanner;

public class MainThread {
    public static void addShutdownHook(ListeningThread thread) {
        Runtime.getRuntime().addShutdownHook(new Thread(new ShutdownCleaner(thread)));
    }

    public static void main(String[] args) {
        ListeningThread lt = new ListeningThread();
        Thread listenThread = new Thread(lt, "ListenThread");
        addShutdownHook(lt);
        listenThread.start();

        Scanner sc = new Scanner(System.in);
        sc.next();
        System.exit(0);
    }

    private static class ShutdownCleaner implements Runnable {
        private ListeningThread listenerThread;

        public ShutdownCleaner(ListeningThread lt) {
            this.listenerThread = lt;
        }

        @Override
        public void run() {
            // Shutdown runs
            if (listenerThread != null) {
                listenerThread.stopThread();
            }
        }
    }
}

当主线程关闭时,它会运行 ShutdownCleaner 线程。

How do I force the background listening thread to quit when the user directs the main thread to terminate?

你应该打断话题。 WatchService.take() 方法抛出 InterruptedException。这意味着当主线程完成后,它可以中断监视服务线程。这将导致 take() 方法抛出一个 InterruptedException,这使监视线程有机会清理并退出 while 循环,然后 return 来自 run()方法。

Thread watchThread = new Thread(new WatchRunnable());
watchThread.start();
// ...
// when the main thread wants the program to stop, it just interrupts the thread
watchThread.interrupt();
// although not necessary, main thread may wait for the watch-thread to finish
watchThread.join();

当你捕获InterruptedException时,重新中断线程总是一个很好的模式,这样调用者也可以使用中断状态:

WatchKey wk;
try {
   wk = ws.take();
} catch (InterruptedException ie) {
   // always recommended
   Thread.currentThread().interrupt();
   // leave the loop or maybe return from run()
   break;
}

重要的是要注意中断线程并不是什么神奇的调用。它在线程上设置一个中断标志,导致某些显式抛出 InterruptedException 的方法抛出。如果您的代码的其他部分想要查看线程是否已被中断,它们应该执行类似以下的操作。

if (Thread.currentThread().isInterrupted()) {
    // clean up and exit the thread...
    break;
}

另一个解决方案就是让观察线程成为守护线程。这意味着当主线程完成时,JVM 将立即退出,而不是等待 watchThread 完成。它不如上面的中断好,因为它可能会在执行一些重要文件 IO 的中间停止 watchThread

Thread watchThread = new Thread(new WatchRunnable());
watchThread.setDaemon(true);
watchThread.start();

我遇到了同样的问题,我发现这个解决方案有效,

解决方案是覆盖中断方法(您的 class 应该扩展 Thread 而不是 Runnable)并从那里关闭 watchService,这将在您的 take() 方法中抛出 ClosedWatchServiceException,因此您必须捕获这个例外

覆盖中断

@Override
public void interrupt() {
    super.interrupt();
    System.out.println("Interupt is called");
    try {

            watcher.close();
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

处理异常

try {
            key = watcher.take();
        } catch (InterruptedException x) {
            System.out.println("take() is interrupted");
            Thread.currentThread().interrupt();
            return;
        } catch (ClosedWatchServiceException e) {
            System.out.println(" take is killed from closedException");
            return;
        }

主线程

public static void main(String[] args) throws InterruptedException {
    WatchDir watchDir = new WatchDir(path);
    Thread.currentThread().sleep(1000);
    watchDir.shutDown();
    watchDir.interrupt();
    watchDir.join();
    System.out.println("the WatchDir thread is killed");
}