这是我第一次进入手表服务和多线程。
我有一个应用程序,需要检测何时外部设备向用户做某事提示。该提示是通过XML文件掉入特定文件夹的方式传递的。该文件始终标题为" prm.xml",如果存在,将覆盖以前的PRM文件。为了防止重新显示旧提示,我的代码显示后删除PRM文件。
该应用程序是用户相互作用的,因此当用户在主线程上执行其他活动时,它始终在背景线程中收听prm.xml。
问题是,当用户想在主线程中结束他的会话时(通过键入前哨" 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.take()
方法抛出InterruptedException
。这意味着,当主线程完成时,它可以中断手表服务线程。这将导致take()
方法抛出InterruptedException
,这使手表线有机会清理和退出while
循环,然后从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
完成。它不如上述中断那么好,因为它可能在其中中间停止watchThread
进行一些重要的文件io。
Thread watchThread = new Thread(new WatchRunnable());
watchThread.setDaemon(true);
watchThread.start();
我遇到了同样的问题,我发现该解决方案正在起作用,
解决方案是覆盖中断方法(您的类应扩展线程而不是可运行的线程)并从那里关闭WatchService,这将在您的take()方法中扔掉loteWatchServiceException,因此您必须捕获此例外
>覆盖中断
@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。
1。使WatchService Closible
应允许手表服务在实现聆听线程的类中关闭。
public void stopThread() {
try {
System.out.println("closing the ws");
ws.close();
} catch (IOException e) {
e.printStackTrace();
}
}
2。在应用程序关闭时关闭WatchService
在主线程中,添加关闭挂钩,以便在应用程序关闭时,它将调用听力线程类的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线程。