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 ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- Path: การแทนตำแหน่งไฟล์
- ยูทิลิตี Files: อ่าน เขียน คัดลอก ย้าย
- การเดินสำรวจต้นไม้ไดเรกทอรีด้วย Files.walk
- WatchService: การตรวจติดตามการเปลี่ยนแปลงไฟล์