從程式設計師的角度來看,非同步檔案 IO 允許在不阻塞呼叫執行執行緒的情況下從檔案系統讀/寫資料。也就是說,請求操作的執行緒。
程式設計師一直可以做到這一點。只需生成一個負責執行 IO 操作的新執行緒,並允許使用某種機制將結果傳達給其他執行緒(如未來執行緒)即可。
但是,JDK 透過 java.nio.channels.AsynchronousFileChannel 提供了一個開箱即用的 API。使用它的理由是,某些作業系統可能會為非同步 IO 提供本機支援,即核心本身可以理解並促進非同步檔案操作。
但是:
- 在 Linux 上,AsynchronousFileChannel 只是將阻塞的 IO 操作轉移到執行器服務上。沒有本機支援。
- 與同步 FileChannel 相比,如果提交寫入的速度快於 IO 操作的完成速度,則更容易耗盡可用堆記憶體。
- 作業系統(OS)在說謊。請注意,在較低層次上,作業系統也提出了非同步的概念。一般來說,作業系統在返回 IO 呼叫時,只是以透明方式將資料寫入記憶體緩衝區。只有在此之後,它們才會真正持續寫入物理磁碟。
門面背後
AsynchronousFileChannel 能使您的應用程式反應更快。如果應用程式是面向使用者的,程式碼可以在讀寫完成前返回給使用者,這樣使用者就可以做其他事情了。
但我們為什麼需要特殊功能呢?為什麼不直接在不同的執行緒中進行阻塞讀/寫操作,並使用未來作為操作完成後返回 "訊號 "的機制呢?
原因是 AsynchronousFileChannel 提供了一個門面,在這個門面後面,一些平臺可以使用不需要執行緒的本地作業系統功能,因此更具可擴充套件性--因為執行緒是有成本的。
API
讓我們先看看如何使用它。我們的重點是檔案 IO 的併發方面,因此我們只對 1) 開啟檔案、2) 讀取或寫入檔案以及 3) 關閉檔案感興趣。非同步檔案通道(AsynchronousFileChannel)還有其他功能,比如鎖定檔案,我們將忽略這些功能。
詳細點選標題
測試結果:
測試持續時間隨著檔案的大小大致線性增長。這證實了非同步檔案通道只是將 IO 操作轉移到另一個執行執行緒上,這可能令大多數人感到驚訝。它沒有任何“本機”支援。
重要的是,寫入後關閉開啟的檔案是一個相對較快的操作。由於作業系統在向呼叫 JVM 應用程式報告 IO 操作已完成之前正在寫入物理磁碟,因此在關閉檔案時,沒有任何內容需要寫入。
總結:
在 Linux 上,Java 的 AsynchronousFileChannel 不支援真正的非同步檔案 IO。相反,它會將同步 IO 操作傳送到你作為引數傳遞的公共執行緒池上。
非同步有兩個來源。一個是本文的主要目標 AsynchronousFileChannel。另一個原因是作業系統先寫入記憶體緩衝區,然後(在返回應用程式呼叫後)才與物理裝置同步。這些機制是獨立的,但你可以開啟一個檔案通道,指示作業系統繞過這些緩衝區。