成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

JDK的一個Bug,監聽文件變更要小心了

開發 前端
本文實踐了一個很常見的功能,起初采用很符合常規思路的方案來解決,結果恰好碰到了JDK的Bug,只好變更策略來實現。

背景

在某些業務場景下,我們需要自己實現文件內容變更監聽的功能,比如:監聽某個文件是否發生變更,當變更時重新加載文件的內容。

看似比較簡單的一個功能,但如果在某些JDK版本下,可能會出現意想不到的Bug。

本篇文章就帶大家簡單實現一個對應的功能,并分析一下對應的Bug和優缺點。

初步實現思路

監聽文件變動并讀取文件,簡單的思路如下:

  • 單起一個線程,定時獲取文件最后更新的時間戳(單位:毫秒);
  • 對比上一次的時間戳,如果不一致,則說明文件被改動,則重新進行加載;

這里寫一個簡單功能實現(不包含定時任務部分)的demo:

public class FileWatchDemo {

/**
* 上次更新時間
*/
public static long LAST_TIME = 0L;

public static void main(String[] args) throws IOException {

String fileName = "/Users/zzs/temp/1.txt";
// 創建文件,僅為實例,實踐中由其他程序觸發文件的變更
createFile(fileName);

// 執行2次
for (int i = 0; i < 2; i++) {
long timestamp = readLastModified(fileName);
if (timestamp != LAST_TIME) {
System.out.println("文件已被更新:" + timestamp);
LAST_TIME = timestamp;
// 重新加載,文件內容
} 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();
}
}

在上述代碼中,先創建一個文件(方便測試),然后兩次讀取文件的修改時間,并用LAST_TIME記錄上次修改時間。如果文件的最新更改時間與上一次不一致,則更新修改時間,并進行業務處理。

示例代碼中for循環兩次,便是為了演示變更與不變更的兩種情況。執行程序,打印日志如下:

文件已被更新:1653557504000
文件未更新

執行結果符合預期。

這種解決方案很明顯有兩個缺點:

  • 無法實時感知文件的變動,程序輪訓畢竟有一個時間差;
  • lastModified返回的時間單位是毫秒,如果同一毫秒內容出現兩次改動,而定時任務查詢時恰好落在兩次變動之間,則后一次變動則無法被感知到。

第一個缺點,對業務的影響不大;第二個缺點的概率比較小,可以忽略不計;

JDK的Bug登場

上面的代碼實現,正常情況下是沒什么問題的,但如果你使用的Java版本為8或9時,則可能出現意想不到的Bug,這是由JDK本身的Bug導致的。

編號為JDK-8177809的Bug是這樣描述的:

Bug地址為:https://bugs.java.com/bugdatabase/view_bug.do?bug_id=8177809

這個Bug的基本描述就是:在Java8和9的某些版本下,lastModified方法返回時間戳并不是毫秒,而是秒,也就是說返回結果的后三位始終為0。

我們來寫一個程序驗證一下:

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);
// 睡眠100ms
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 {
FileWriter fileWriter = new FileWriter(fileName);
// 寫入隨機數字
fileWriter.write(new Random(1000).nextInt());
fileWriter.close();
}

public static long readLastModified(String fileName) {
File file = new File(fileName);
return file.lastModified();
}
}

在上述代碼中,先創建一個文件,然后在for循環中不停的向文件寫入內容,并讀取修改時間。每次操作睡眠100ms。這樣,同一秒就可以多次寫文件和讀修改時間。

執行結果如下:

文件修改時間:1653558619000
文件修改時間:1653558619000
文件修改時間:1653558619000
文件修改時間:1653558619000
文件修改時間:1653558619000
文件修改時間:1653558619000
文件修改時間:1653558620000
文件修改時間:1653558620000
文件修改時間:1653558620000
文件修改時間:1653558620000

修改了10次文件的內容,只感知到了2次。JDK的這個bug讓這種實現方式的第2個缺點無限放大了,同一秒發生變更的概率可比同一毫秒發生的概率要大太多了。

PS:在官方Bug描述中提到可以通過Files.getLastModifiedTime來實現獲取時間戳,但筆者驗證的結果是依舊無效,可能不同版本有不同的表現吧。

更新解決方案

Java 8目前是主流版本,不可能因為JDK的該bug就換JDK吧。所以,我們要通過其他方式來實現這個業務功能,那就是新增一個用來記錄文件版本(version)的文件(或其他存儲方式)。這個version的值,可在寫文件時按照遞增生成版本號,也可以通過對文件的內容做MD5計算獲得。

如果能保證版本順序生成,使用時只需讀取版本文件中的值進行比對即可,如果變更則重新加載,如果未變更則不做處理。

如果使用MD5的形式,則需考慮MD5算法的性能,以及MD5結果的碰撞(概率很小,可以忽略)。

下面以版本的形式來展示一下demo:

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("版本已變化,進行業務處理");
}
// 睡眠100ms
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(1000).nextInt());
}

public static void writeToFile(String fileName, int version) throws IOException {
FileWriter fileWriter = new FileWriter(fileName);
fileWriter.write(version +"");
fileWriter.close();
}

public static String readOneLineFromFile(String fileName) {
File file = new File(fileName);
String tempString = null;
try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
//一次讀一行,讀入null時文件結束
tempString = reader.readLine();
} catch (IOException e) {
e.printStackTrace();
}
return tempString;
}
}

執行上述代碼,打印日志如下:

版本已變化,進行業務處理
版本已變化,進行業務處理
版本已變化,進行業務處理
版本已變化,進行業務處理
版本已變化,進行業務處理
版本已變化,進行業務處理
版本已變化,進行業務處理
版本已變化,進行業務處理
版本已變化,進行業務處理

可以看到,每次文件變更都能夠感知到。當然,上述代碼只是示例,在使用的過程中還是需要更多地完善邏輯。

小結

本文實踐了一個很常見的功能,起初采用很符合常規思路的方案來解決,結果恰好碰到了JDK的Bug,只好變更策略來實現。當然,如果業務環境中已經存在了一些基礎的中間件還有更多解決方案。

而通過本篇文章我們學到了JDK Bug導致的連鎖反應,同時也見證了:實踐見真知。很多技術方案是否可行,還是需要經得起實踐的考驗才行。趕快檢查一下你的代碼實現,是否命中該Bug?

責任編輯:武曉燕 來源: 程序新視界
相關推薦

2022-05-16 08:42:26

Pandasbug

2021-09-13 08:41:52

職場互聯網自閉

2020-01-10 09:20:03

手機ISOJDK

2018-06-20 15:50:38

JDK9JVMJDK10

2020-09-29 07:44:20

跨域前后端分離插件

2025-02-13 07:00:00

Dubbo-goJava服務端

2009-09-14 17:08:02

WebFormView

2022-11-30 09:18:51

JavaMyBatisMQ

2021-04-22 07:47:47

JavaJDKMYSQL

2022-05-10 12:20:04

JDKversion故障

2011-07-29 16:55:44

Java 7

2021-10-08 07:50:57

軟件設計程序

2017-10-10 15:14:23

BUGiOS 11蘋果

2016-09-28 14:00:56

2023-01-26 11:43:03

線程池CPUJava

2015-01-23 10:04:56

bug程序員

2024-10-25 12:38:27

2023-06-20 08:25:53

NESTED源碼mybatis

2014-12-17 09:40:22

dockerLinuxPaaS

2023-05-09 11:02:22

Go內聯版本
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 欧美久久视频 | 久久久久亚洲精品 | av一区在线观看 | 久久成人一区 | 天天色综| 人人干人人超 | 精品99在线| 中文天堂在线观看 | 日韩欧美精品在线播放 | 日韩中文字幕在线视频观看 | 久久成人精品 | av在线播放一区二区 | 久久国产精品72免费观看 | 欧美老少妇一级特黄一片 | 免费亚洲网站 | 亚洲 精品 综合 精品 自拍 | 午夜视频一区二区三区 | 亚洲久草 | 超碰在线人人 | 国产激情自拍视频 | 国产欧美视频一区二区三区 | 成人超碰在线 | 超碰日本 | 91精品国产乱码久久久久久久久 | 一区二区三区精品视频 | 亚洲色图综合 | 色视频网站在线观看 | 999久久久 | 操操日| 免费精品视频一区 | 在线观看免费观看在线91 | 精精国产xxxx视频在线播放 | 精品欧美一区二区三区久久久 | 国产1页 | 日韩精品一区二区三区在线观看 | 日韩在线免费视频 | 国产亚洲网站 | 精品国产青草久久久久福利 | 欧美一区二区三区在线观看 | 国产精品a久久久久 | 精品国产乱码久久久久久影片 |