如何终止检测新文件的连续线程
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
尝试在主线程中添加关闭钩子,这样当主应用程序退出时,您也可以正确终止监听线程。
待完成的更改
根据您的代码,需要进行以下更改:
- 允许从主线程关闭 WatchService
- 向 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");
}
这是我第一次涉足 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
尝试在主线程中添加关闭钩子,这样当主应用程序退出时,您也可以正确终止监听线程。
待完成的更改
根据您的代码,需要进行以下更改:
- 允许从主线程关闭 WatchService
- 向 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");
}