當年偶然發現的 Java Bug(JDK 9及之前仍未修復)
阿新 • • 發佈:2020-09-05
# 背景
15年在中信銀行做持續整合時,由於當時的專案是基於三方採購的 Java 配置開發平臺做的,平臺自己基於 ```Ant``` 外掛實現了增量和熱部署。
其中有幾個專案在持續整合部署時,經常發現 ```Linux``` 平臺部署成功後(```Windows``` 不會出現,```Linux``` 也是偶發現象),新版本程式碼並沒有生效(反編譯 class)。
起初我是在本地 ```windows``` 上跟蹤除錯基於 ```Ant``` 外掛的程式碼,但始終重現不了(最後測試發現 Windows 無此 Bug)。
後來,通過分析程式碼邏輯,其中有段邏輯是通過檔案的最後修改時間(```File.lastModified()```)來判斷要不要覆蓋部署的,最後通過單測發現,是由於 ```Java``` 的 ```File.lastModified()``` 方法在 ```Windows``` 和 ```Linux/Unix``` 平臺獲取的精度不一樣導致的,```Windows``` 精度為毫秒,而 ```Linux/Unix``` 只能到秒(JDK Bug:JDK-8177809)。
所以也解釋了,為什麼是偶發現象,檔案修改時間如果判斷的兩個值正好跨秒時,部署就是成功的,否則失敗。
# Bug 重現
**測試程式碼:FileTest.java**
```java
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.text.SimpleDateFormat;
public class FileTest {
private static final long LM = 1599276952718L;
public static void main(String[] args) throws IOException {
// java版本號
System.out.println("Java Version:" + System.getProperty("java.version"));
File f = new File("test.txt");
f.createNewFile();
// 設定最後修改時間
f.setLastModified(LM);
// 獲取修改時間,存在 bug
System.out.printf("Test f.lastModified [%s]: %b\n",
f.lastModified(), f.lastModified() == LM);
// 格式化輸出,正確不存在 bug
System.out.printf("Test f.lastModified DateFormat [%s]\n",new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.sss").format(f.lastModified()));
// Files.getLastModifiedTime() 獲取修改時間,同樣存在 bug
System.out.printf("Test Files.getLastModifiedTime [%s]: %b\n",
Files.getLastModifiedTime(f.toPath()).toMillis(),
(Files.getLastModifiedTime(f.toPath()).toMillis() == LM));
// 格式化輸出,正確不存在 bug
System.out.printf("Test Files.getLastModifiedTime DateFormat [%s]\n",new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.sss").format(f.lastModified()));
f.delete();
}
}
```
命令列下編譯、執行:
```bash
# 編譯執行
$ javac FileTest.java && java FileTest
```
## Windows 執行結果
Windows 平臺不存在此 Bug。
```bash
# 編譯執行
$ javac FileTest.java && java FileTest
Java Version:1.8.0_202
Test f.lastModified [1599276952718]: true
Test f.lastModified DateFormat [2020-09-05 11:35:52.052]
Test Files.getLastModifiedTime [1599276952718]: true
Test Files.getLastModifiedTime DateFormat [2020-09-05 11:35:52.052]
```
## Mac 執行結果
JDK 8 最新版本,目前仍然沒有修復該問題。
```bash
# 編譯執行
$ javac FileTest.java && java FileTest
Java Version:1.8.0_261
Test f.lastModified [1599276952000]: false
Test f.lastModified DateFormat [2020-09-05 11:35:52.052]
Test Files.getLastModifiedTime [1599276952000]: false
Test Files.getLastModifiedTime DateFormat [2020-09-05 11:35:52.052]
```
## Linux 執行結果
```bash
# 編譯執行
$ javac FileTest.java && java FileTest
Java Version:1.8.0_171
Test f.lastModified [1599276952000]: false
Test f.lastModified DateFormat [2020-09-05 11:35:52.052]
Test Files.getLastModifiedTime [1599276952000]: false
Test Files.getLastModifiedTime DateFormat [2020-09-05 11:35:52.052]
```
# 官方 Bug 連結
JDK10 已修復,但是之前版本目前仍然未修復。
- https://bugs.openjdk.java.net/browse/JDK-8177809
> 微信公眾號:da