In my previous question I was warking on a simple exercise that watched a directory for file changes. I took the code from this oracle docs, and it worked with no problem, except for the little unchecked cast warning I was unsure about.
The next problem I have with this code is that it's putting a hard loop inside a thread, which is blocking, at least in theory. Now, I know that if the operating system uses time slicing, even the hard loop is split up in small chunks that share the processor's time with the other threads the application is running, and in fact I can make all sorts of examples where hard loops running in different threads don't block each other (as long as they have the same priority), even on a virtual machine expressly created with only one core.
However, the Java language doesn't guarantee which kind of scheduling it uses for thread management, if it's time-slicing or round-robin; it depends on the actual VM implementation and operating system. So the advice I'm getting while studying the subject is to write code as if it had to run on a round-robin thread scheduling, thus avoiding putting hard loops in threads, unless my code can continuously yeld back the control to the other threads with sleep()
, wait()
, yeld()
, etc. (I can think of a GUI where the main thread is the one with a hard loop watching for events, and sending control back to listeners to handle them).
In my case, however, I couldn't think of a way to put the thread to sleep after it handled the file change, or to yeld the control back to the main loop, since the core idea is basically to continuously ask if there are changes to the filesystem. So what I came up to, is a scheduled executor that regularly calls the watching thread. Clearly, this is a tradeoff between having a "blocking" thread, and being immediately notified when a filesystem change happens. Since in the real case I'm going to put this exercise into, I probably won't need immedite notification, I'm happy with that. The code is very straightforward:
// imports...
public class Main
{
public static FileSystem fs;
public static Path dir;
public static WatchService watcher;
public static WatchKey key;
public static void main(String[] args)
{
fs = FileSystem.getDefault();
dir = fs.getPath(".");
try {
watcher = fs.newWatchService();
dir.register(watcher, StandardWatchEventKinds.ENTRY_MODIFY);
} catch (IOException e) {
System.err.println(e.getMessage());
return;
}
Executors.newScheduledThreadPool(1).scheduleAtFixedRate(new Runnable()
{
public void run()
{
Main.key = Main.watcher.poll();
if (null == Main.key)
return;
for (WatchEvent<?> event : Main.key.pollEvents()) {
WatchEvent.Kind<?> kind = event.kind();
if (kind == StandardWatchEventKinds.OVERFLOW)
continue;
@SuppressWarnings("unchecked");
WatchEvent<Path> ev = (WatchEvent<Path>)event;
Path file = ev.context();
System.out.println(file);
boolean valid = Main.key.reset();
if (!valid)
System.err.println("Invalid key!");
}
}
}, 0, 1, TimeUnit.SECONDS);
}
}
So my questions are:
Am I pushing this too far? I mean, is it actually a good practice to care this much about blocking code in threads, or are the real cases where time slicing is not present, so rare, that I can safely put a hard loop inside a thread, and maybe do this kind of stuff only when I know that my code is going to run maybe on an embedded device with guaranteed round-robin?
Is there any other way to avoid the hard loop in this particular case? Maybe some clever use of thread control methods (
sleep()
,wait()
, etc.), that I can't think of?
Thank you very much, and sorry for the long post.