Detecting File Changes in Java and Bypassing the JDK‑8177809 Bug
This article walks through a simple Java file‑change watcher, reveals a JDK 8/9 bug where lastModified returns seconds instead of milliseconds, demonstrates the issue with test code, and presents a version‑file workaround to reliably detect updates.
Background
In some scenarios a program must detect when a file is modified and reload its content. A simple approach is to poll the file’s last‑modified timestamp and compare it with the previous value.
Initial implementation
The basic idea is to run a thread that periodically calls File.lastModified() (which returns a millisecond value) and reloads the file when the timestamp changes.
public class FileWatchDemo {
/** last update time */
public static long LAST_TIME = 0L;
public static void main(String[] args) throws IOException {
String fileName = "/Users/zzs/temp/1.txt";
createFile(fileName);
// demo: two checks
for (int i = 0; i < 2; i++) {
long timestamp = readLastModified(fileName);
if (timestamp != LAST_TIME) {
System.out.println("文件已被更新:" + timestamp);
LAST_TIME = timestamp;
// reload file content here
} else {
System.out.println("文件未更新");
}
}
}
public static void createFile(String fileName) throws IOException {
File file = new File(fileName);
if (!file.exists()) {
boolean result = file.createNewFile();
System.out.println("创建文件:" + result);
}
}
public static long readLastModified(String fileName) {
File file = new File(fileName);
return file.lastModified();
}
}Running the demo prints a message when the file is first created and a second message when no change is detected. The approach has two drawbacks:
Polling introduces latency; changes are not detected in real time.
If two modifications occur within the same millisecond, the second change may be missed because the timestamp granularity is only milliseconds.
JDK bug
On certain builds of Java 8 and Java 9 the method File.lastModified() incorrectly returns seconds instead of milliseconds, leaving the last three digits always zero. This is tracked as Bug ID JDK‑8177809 (URL: https://bugs.java.com/bugdatabase/view_bug.do?bug_id=8177809).
Verification code:
public class FileReadDemo {
public static void main(String[] args) throws IOException, InterruptedException {
String fileName = "/Users/zzs/temp/1.txt";
createFile(fileName);
for (int i = 0; i < 10; i++) {
writeToFile(fileName);
long timestamp = readLastModified(fileName);
System.out.println("文件修改时间:" + timestamp);
Thread.sleep(100);
}
}
// createFile, writeToFile, readLastModified omitted for brevity
}On a buggy JDK the output shows identical timestamps for several iterations, e.g.:
文件修改时间:1653558619000
文件修改时间:1653558619000
文件修改时间:1653558619000
文件修改时间:1653558619000
文件修改时间:1653558619000
文件修改时间:1653558619000
文件修改时间:1653558620000
文件修改时间:1653558620000
文件修改时间:1653558620000
文件修改时间:1653558620000Only two of the ten modifications are detected, magnifying the second drawback of the polling approach.
Updated solution
Because many projects still run on Java 8, replacing the JDK is not practical. A reliable alternative is to maintain a separate version indicator—either a monotonically increasing number or an MD5 hash of the file content. The indicator is written whenever the target file is updated; the watcher reads the indicator and reloads the file only when the version differs.
Demo using a version file (incremental integer):
public class FileReadVersionDemo {
public static int version = 0;
public static void main(String[] args) throws IOException, InterruptedException {
String fileName = "/Users/zzs/temp/1.txt";
String versionName = "/Users/zzs/temp/version.txt";
createFile(fileName);
createFile(versionName);
for (int i = 1; i < 10; i++) {
writeToFile(fileName);
writeToFile(versionName, i);
int fileVersion = Integer.parseInt(readOneLineFromFile(versionName));
if (version == fileVersion) {
System.out.println("版本未变更");
} else {
System.out.println("版本已变化,进行业务处理");
version = fileVersion;
}
Thread.sleep(100);
}
}
public static void createFile(String fileName) throws IOException {
File file = new File(fileName);
if (!file.exists()) {
boolean result = file.createNewFile();
System.out.println("创建文件:" + result);
}
}
public static void writeToFile(String fileName) throws IOException {
writeToFile(fileName, new Random().nextInt());
}
public static void writeToFile(String fileName, int value) throws IOException {
FileWriter fw = new FileWriter(fileName);
fw.write(String.valueOf(value));
fw.close();
}
public static String readOneLineFromFile(String fileName) {
File file = new File(fileName);
try (BufferedReader br = new BufferedReader(new FileReader(file))) {
return br.readLine();
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
}Running this program prints “版本已变化,进行业务处理” on every iteration, confirming that each modification is detected.
Conclusion
The article shows how a naïve polling implementation for file change detection can fail on certain JDK versions due to a bug that reduces timestamp precision to seconds. Replacing the timestamp check with an explicit version indicator (numeric counter or hash) provides reliable detection without depending on the JDK’s lastModified behavior.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Senior Brother's Insights
A public account focused on workplace, career growth, team management, and self-improvement. The author is the writer of books including 'SpringBoot Technology Insider' and 'Drools 8 Rule Engine: Core Technology and Practice'.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
