0Pricing
Java Academy · Lesson

WatchService: Monitoring File Changes

Register a WatchService to detect file creation, modification, and deletion events in real time.

WatchService: Monitoring File Changes is a free Java Academy lesson on CoddyKit — lesson 4 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Java Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

What is WatchService?

WatchService monitors a directory for file system events: creation, modification, and deletion. It uses OS-level notifications (inotify on Linux, FSEvents on macOS) — much more efficient than polling.

import java.nio.file.*;

WatchService watcher = FileSystems.getDefault().newWatchService();
Path dir = Path.of("/home/user/watched");

dir.register(watcher,
    StandardWatchEventKinds.ENTRY_CREATE,
    StandardWatchEventKinds.ENTRY_MODIFY,
    StandardWatchEventKinds.ENTRY_DELETE
);
System.out.println("Watching: " + dir);

Polling for Events

Call watcher.take() to block until an event occurs, then process the events:

while (true) {
    WatchKey key;
    try {
        key = watcher.take(); // blocks until events arrive
    } catch (InterruptedException e) {
        break;
    }
    
    for (WatchEvent<?> event : key.pollEvents()) {
        WatchEvent.Kind<?> kind = event.kind();
        Path changed = ((WatchEvent<Path>) event).context();
        System.out.println(kind.name() + ": " + changed);
    }
    
    key.reset(); // must reset to receive further events
}

Non-blocking Poll

Use watcher.poll() for non-blocking checks, or poll(timeout, unit) for timed waits:

// Non-blocking — returns null if no events
WatchKey key = watcher.poll();
if (key != null) {
    // process events
    key.reset();
}

// Wait up to 5 seconds:
WatchKey key2 = watcher.poll(5, java.util.concurrent.TimeUnit.SECONDS);
if (key2 != null) {
    // process events
    key2.reset();
}

OVERFLOW Event

If events accumulate faster than the application processes them, an OVERFLOW event signals that some events were lost. Handle it gracefully:

for (WatchEvent<?> event : key.pollEvents()) {
    if (event.kind() == StandardWatchEventKinds.OVERFLOW) {
        System.out.println("OVERFLOW: some events missed, rescanning directory");
        // rescan dir to find current state
        continue;
    }
    // process normal events
}

Full Working Example

A complete file watcher that runs on a background thread:

class FileWatcher implements Runnable {
    private final WatchService watcher;
    
    FileWatcher(Path dir) throws IOException {
        watcher = FileSystems.getDefault().newWatchService();
        dir.register(watcher,
            StandardWatchEventKinds.ENTRY_CREATE,
            StandardWatchEventKinds.ENTRY_MODIFY,
            StandardWatchEventKinds.ENTRY_DELETE);
    }
    
    @Override
    public void run() {
        while (!Thread.currentThread().isInterrupted()) {
            try {
                WatchKey key = watcher.take();
                for (WatchEvent<?> e : key.pollEvents())
                    System.out.println(e.kind() + ": " + e.context());
                key.reset();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }
}

Watching Multiple Directories

Register multiple directories with the same WatchService. Use the returned WatchKey to identify which directory the event came from:

Map<WatchKey, Path> keyMap = new HashMap<>();

for (Path dir : List.of(Path.of("/logs"), Path.of("/config"))) {
    WatchKey key = dir.register(watcher,
        StandardWatchEventKinds.ENTRY_MODIFY);
    keyMap.put(key, dir);
}

WatchKey key = watcher.take();
Path dir = keyMap.get(key);
System.out.println("Changed in: " + dir);

Resolving Full Path

The event's context() returns only the filename, not the full path. Resolve it against the watched directory:

WatchEvent<Path> pathEvent = (WatchEvent<Path>) event;
Path filename = pathEvent.context();
Path fullPath = watchedDir.resolve(filename);
System.out.println("Full path: " + fullPath);

Recursive Directory Watching

WatchService watches only the registered directory, not its subdirectories. For recursive watching, walk the tree and register each subdirectory:

WatchService watcher = FileSystems.getDefault().newWatchService();
Path root = Path.of("/project");

try (var stream = Files.walk(root)) {
    stream.filter(Files::isDirectory)
          .forEach(dir -> {
              try { dir.register(watcher, StandardWatchEventKinds.ENTRY_CREATE,
                  StandardWatchEventKinds.ENTRY_MODIFY, StandardWatchEventKinds.ENTRY_DELETE);
              } catch (IOException e) { throw new RuntimeException(e); }
          });
}

Resource Cleanup

Always close the WatchService when done to release OS resources:

try (WatchService watcher = FileSystems.getDefault().newWatchService()) {
    Path dir = Path.of("/tmp/watch");
    dir.register(watcher, StandardWatchEventKinds.ENTRY_CREATE);
    
    WatchKey key = watcher.poll(10, java.util.concurrent.TimeUnit.SECONDS);
    if (key != null) {
        key.pollEvents().forEach(e -> System.out.println(e.context()));
        key.reset();
    }
} // watcher.close() called automatically

Practical Use Case: Hot Reload Config

Use WatchService to reload configuration when the file changes:

class ConfigWatcher {
    private volatile Map<String, String> config = new HashMap<>();
    
    void loadConfig(Path file) throws IOException {
        config = Files.readAllLines(file).stream()
            .filter(l -> l.contains("="))
            .collect(Collectors.toMap(
                l -> l.split("=")[0].trim(),
                l -> l.split("=")[1].trim(),
                (a,b) -> b
            ));
    }
    
    // Call loadConfig again on ENTRY_MODIFY event
}

WatchService Limitations

Important limitations:

  • Watches directories only, not individual files
  • Not recursive by default — register each subdir manually
  • On macOS: uses polling internally (less responsive than Linux inotify)
  • May miss rapid bursts of changes (OVERFLOW)
  • WatchKey becomes invalid if the watched directory is deleted

Quick Check

After processing events from a WatchKey, what must you call before the WatchService will deliver further events for that key?

Recap: WatchService

Key takeaways:

  • Register directories with ENTRY_CREATE/MODIFY/DELETE events
  • take() blocks; poll() is non-blocking; poll(timeout, unit) for timed wait
  • Always call key.reset() after processing events
  • context() returns filename only — resolve against watched dir for full path
  • Close WatchService with try-with-resources
  • Recursive watching requires registering each subdirectory

Frequently asked questions

Is the “WatchService: Monitoring File Changes” lesson free?

Yes — the full text of “WatchService: Monitoring File Changes” is free to read here on the web, and the Java Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Java Academy course, upgrade to CoddyKit PRO.

What will I learn in “WatchService: Monitoring File Changes”?

Register a WatchService to detect file creation, modification, and deletion events in real time. You practise Java Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start Java Academy?

No prior experience is required. Java Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 4 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “WatchService: Monitoring File Changes” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this Java Academy lesson?

Yes. Every Java Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. Path: Representing File Locations
  2. Files Utility: Read, Write, Copy, Move
  3. Walking Directory Trees with Files.walk
  4. WatchService: Monitoring File Changes
← Back to Java Academy