0Pricing
Java Academy · บทเรียน

WatchService: การตรวจติดตามการเปลี่ยนแปลงไฟล์

ลงทะเบียน WatchService เพื่อตรวจจับเหตุการณ์การสร้าง การแก้ไข และการลบไฟล์แบบเรียลไทม์

WatchService: การตรวจติดตามการเปลี่ยนแปลงไฟล์ เป็นบทเรียน Java Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Java Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Java Academy มีบทเรียนทั้งหมด 4 บทเรียน

WatchService คืออะไร

WatchService เฝ้าติดตามไดเรกทอรีเพื่อดูเหตุการณ์ของระบบไฟล์ ได้แก่ การสร้าง การแก้ไข และการลบ โดยใช้การแจ้งเตือนระดับ OS (inotify บน Linux และ FSEvents บน macOS) ซึ่งมีประสิทธิภาพสูงกว่าการตรวจสอบเป็นระยะมาก

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);

การตรวจสอบเหตุการณ์เป็นระยะ

เรียกใช้ watcher.take() เพื่อหยุดรอจนกว่าจะมีเหตุการณ์เกิดขึ้น จากนั้นจึงประมวลผลเหตุการณ์ดังกล่าว

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
}

การตรวจสอบแบบไม่หยุดรอ

ใช้ watcher.poll() เพื่อตรวจสอบโดยไม่หยุดรอ หรือใช้ poll(timeout, unit) เพื่อรอเป็นระยะเวลาที่กำหนด

// 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

หากเหตุการณ์สะสมเร็วกว่าที่แอปพลิเคชันจะประมวลผลได้ เหตุการณ์ OVERFLOW จะแจ้งว่ามีเหตุการณ์บางส่วนสูญหาย ให้จัดการกรณีนี้อย่างเหมาะสม

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
}

ตัวอย่างการทำงานแบบสมบูรณ์

ตัวเฝ้าดูไฟล์แบบสมบูรณ์ที่ทำงานบนเธรดเบื้องหลัง

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();
            }
        }
    }
}

การเฝ้าดูหลายไดเรกทอรี

ลงทะเบียนไดเรกทอรีหลายรายการด้วย WatchService เดียวกัน ใช้ WatchKey ที่ส่งคืนเพื่อระบุว่าเหตุการณ์มาจากไดเรกทอรีใด

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);

การหาตำแหน่งเส้นทางเต็ม

context() ของเหตุการณ์จะส่งคืนเฉพาะชื่อไฟล์ ไม่ใช่เส้นทางเต็ม ให้รวมเส้นทางนั้นเข้ากับไดเรกทอรีที่เฝ้าดู

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

การเฝ้าดูไดเรกทอรีแบบ递归

WatchService จะเฝ้าดูเฉพาะไดเรกทอรีที่ลงทะเบียนไว้ ไม่รวมไดเรกทอรีย่อย หากต้องการเฝ้าดูแบบ递归 ให้เดินสำรวจโครงสร้างต้นไม้และลงทะเบียนไดเรกทอรีย่อยแต่ละรายการ

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); }
          });
}

การล้างทรัพยากร

ปิด WatchService เสมอเมื่อใช้งานเสร็จ เพื่อคืนทรัพยากรของ OS

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

กรณีใช้งานจริง: โหลดการตั้งค่าใหม่ทันที

ใช้ WatchService เพื่อโหลดการตั้งค่าใหม่เมื่อไฟล์มีการเปลี่ยนแปลง

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

ข้อจำกัดสำคัญมีดังนี้

  • เฝ้าดูได้เฉพาะไดเรกทอรี ไม่ใช่ไฟล์แต่ละไฟล์
  • ไม่ได้ทำงานแบบ递归โดยค่าเริ่มต้น ต้องลงทะเบียนไดเรกทอรีย่อยแต่ละรายการด้วยตนเอง
  • บน macOS จะใช้การตรวจสอบเป็นระยะภายใน จึงตอบสนองช้ากว่า inotify บน Linux
  • อาจพลาดการเปลี่ยนแปลงที่เกิดขึ้นอย่างรวดเร็วเป็นชุด ๆ ได้ (OVERFLOW)
  • WatchKey จะไม่ถูกต้องอีกต่อไปหากไดเรกทอรีที่เฝ้าดูถูกลบ

ตรวจสอบความเข้าใจอย่างรวดเร็ว

หลังจากประมวลผลเหตุการณ์จาก WatchKey แล้ว ต้องเรียกใช้สิ่งใดก่อนที่ WatchService จะส่งเหตุการณ์เพิ่มเติมสำหรับคีย์นั้น

สรุป: WatchService

ประเด็นสำคัญที่ควรจำ

  • ลงทะเบียนไดเรกทอรีด้วยเหตุการณ์ ENTRY_CREATE/MODIFY/DELETE
  • take() จะหยุดรอ ส่วน poll() จะไม่หยุดรอ และใช้ poll(timeout, unit) เมื่อต้องการรอตามเวลาที่กำหนด
  • เรียกใช้ key.reset() เสมอหลังประมวลผลเหตุการณ์
  • context() ส่งคืนเฉพาะชื่อไฟล์ ให้รวมเข้ากับไดเรกทอรีที่เฝ้าดูเพื่อให้ได้เส้นทางเต็ม
  • ปิด WatchService ด้วยทรัพยากรแบบลองใช้แล้วปิดอัตโนมัติ
  • การเฝ้าดูแบบ递归ต้องลงทะเบียนไดเรกทอรีย่อยแต่ละรายการ

คำถามที่พบบ่อย

บทเรียน “WatchService: การตรวจติดตามการเปลี่ยนแปลงไฟล์” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “WatchService: การตรวจติดตามการเปลี่ยนแปลงไฟล์” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Java Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Java Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “WatchService: การตรวจติดตามการเปลี่ยนแปลงไฟล์”

ลงทะเบียน WatchService เพื่อตรวจจับเหตุการณ์การสร้าง การแก้ไข และการลบไฟล์แบบเรียลไทม์ คุณปฏิบัติ Java Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Java Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน Java Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน

บทเรียน “WatchService: การตรวจติดตามการเปลี่ยนแปลงไฟล์” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน Java Academy นี้ได้ไหม

ได้ บทเรียน Java Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. Path: การแทนตำแหน่งไฟล์
  2. ยูทิลิตี Files: อ่าน เขียน คัดลอก ย้าย
  3. การเดินสำรวจต้นไม้ไดเรกทอรีด้วย Files.walk
  4. WatchService: การตรวจติดตามการเปลี่ยนแปลงไฟล์
← กลับไปที่ Java Academy