Polling WatchService in a separate thread
As mentioned earlier in our WatchService tutorial,
we will usually want to poll for file modifications
in a separate thread. This means that we need to pay attention to a few
issues of thread-safety. Key points, some of which
we have touched upon, are as follows:
- we will usually run our polling loop in a separate thread to the thread (often the UI event thread)
which handles the
"logic" of registering paths, displaying modifications etc;
- the Path class is thread-safe (because all of its member variables are final):
we can happily pass around instances of Path without
worrying about thread-safety;
- we must ensure that any reference to our WatchService is final, since it
will be referred to by different threads;
- any object mapping WatchKeys to their corresponding Paths, which will usually
be referred to in both the "logic" thread of your application when registering directories and also
in the processing thread, must be accessed in a thread-safe way, either via synchronization or
by using an inherently thread-safe map such as a
ConcurrentHashMap;
- in your polling loop and modification processing method,
you may need to think about which thread you pass processing to: for example, if you wish to
update a user interface component to notify the user that the file has been modified, you may need
to do so inside an invokeLater() construct.
How to run the processing loop in a separate thread
We will typically run the file modification processing loop in its own thread, e.g. as follows:
private volatile Thread processingThread;
public void startListening() {
processingThread = new Thread() {
public void run() {
try {
processFileNotifications();
} catch (InterruptedException ex();
processingThread = null;
}
};
processingThread.start();
}
public void shutDownListener() {
Thread thr = processingThread;
if (thr != null) {
thr.interrupt();
}
}
Then in our process() method, for example:
private void process(Path dir, Path file, WatchEvent.Kind evtType) {
if (UI needs updating) {
final Path fileRef = file;
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// ... update UI with "fileRef"
}
});
}
}
Notice the slightly awkward syntax here: our update code, inside the embedded run()
method, will run in a separate thread. Therefore, any objects from the process() method
that we also wish to reference from inside the update thread must be refenced via final
references. Hence, we create as an example the final reference fileRef in order to refer
to the path of the file (but we could create similar references to dir, evtType
or any other variable created via process().
When do we not need a separate processing thread?
If your application is going to run as a "standalone" process whose only purpose is to
monitor a particular directory (e.g. system log files) and no user interaction is required,
then you may be able to run the modification processing loop, and possibly your whole application,
in a single thread. The decision will depend on whether you require your application to perform
other tasks while file notification processing is going on.
If you enjoy this Java programming article, please share with friends and colleagues. Follow the author on Twitter for the latest news and rants.
Editorial page content written by Neil Coffey. Copyright © Javamex UK 2021. All rights reserved.