这是我第一次涉足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
答案 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)
尝试在主线程中添加一个关闭钩子,这样当主应用程序退出时,你也可以正确终止监听线程。
从您的代码中,需要进行以下更改:
应该允许在实现监听线程的类中关闭WatchService。
public void stopThread() {
try {
System.out.println("closing the ws");
ws.close();
} catch (IOException e) {
e.printStackTrace();
}
}
在主线程中,添加一个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线程。