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

时间:2017-03-10 19:00:37

标签: java multithreading watchservice

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

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

应用程序是用户交互式的,因此当用户在主线程上执行其他活动时,它总是在后台线程中侦听PRM.xml。

问题是当用户想要在主线程中结束他的会话时(通过键入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

3 个答案:

答案 0 :(得分:2)

  

当用户指示主线程终止时,如何强制后台侦听线程退出?

你应该打断线程。 Nlog(N)方法抛出WatchService.take()。这意味着当主线程完成时,它可以中断监视服务线程。这将导致InterruptedException方法抛出take(),这使得监视线程有机会清理并退出InterruptedException循环,然后从while方法返回。 / p>

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

另一个解决方案就是让观察者线程成为守护程序线程。这意味着当主线程完成时,JVM将立即退出,而不是等待if (Thread.currentThread().isInterrupted()) { // clean up and exit the thread... break; } 完成。它不如上面的中断好,因为它可能会在它执行一些重要文件IO的过程中阻止watchThread

watchThread

答案 1 :(得分:1)

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

解决方案是重写中断方法(您的类应扩展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");
}

答案 2 :(得分:0)

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

要完成的更改

从您的代码中,需要进行以下更改:

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

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

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

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

    在主线程中,添加一个shutdown hook,以便在应用程序关闭时,它将调用侦听线程类的stopThread()方法。

    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线程。