點擊上方「linkoffer」,
選擇關注公眾號高薪職位第一時間送達
來源 | https://urlify.cn/jumuiq當讀寫文件時,需要確保有適當的文件鎖定機制,來保證基於並發I/O應用程式的數據完整性。
本教程中, 我們將介紹使用 Java NIO 庫實現這一點的各種方法。
# 文件鎖簡介「一般來說,有兩種鎖」:
簡單地說,在寫操作完成時,獨佔鎖防止所有其他操作(包括讀操作)。
相反,共享鎖允許多個進程同時讀取。讀鎖的目的是防止另一個進程獲取寫鎖。通常,處於一致狀態的文件確實應該被任何進程讀取。
在下一節中,我們將看到Java如何處理這些類型的鎖。
# Java中的文件鎖Java NIO庫支持在作業系統級別鎖定文件。FileChannel 中的lock() 和*tryLock()*方法就是為了這個而存在。
我們可以通過 FileInputStream, FileOutputStream,RandomAccessFile 來獲取FileChannel,三者均可通過 getChannel() 方法返回 FileChannel對象.
或者, 我們可以直接通過靜態方法 open 來創建 FileChannel :
try (FileChannel channel = FileChannel.open(path, openOptions)) { }
接下來,我們將回顧在Java中獲取獨佔鎖和共享鎖的不同方式。要了解有關文件通道的更多信息,請查看[Guide to Java FileChanne 教程。
# 獨佔鎖正如我們已經了解到的,在寫入文件時,「我們可以使用獨佔鎖」防止其他進程讀取或寫入文件。
我們通過調用 FileChannel 類上的 lock() 或 tryLock()) 來獲得獨佔鎖。我們還可以使用它們的重載方法:
lock(long position, long size, boolean shared)
tryLock(long position, long size, boolean shared)
在這些情況下,shared參數必須設置為false。
要獲得獨佔鎖,必須使用可寫的文件通道。我們可以通過 FileOutputStream 或 RandomAccessFile 的 getChannel() 方法創建它。或者,如前所述,我們可以使用 FileChannel 類的靜態方法:open。我們只需要將第二個參數設置為StandardOpenOption.APPEND :
try (FileChannel channel = FileChannel.open(path, StandardOpenOption.APPEND)) { }1. 使用 FileOutputStream 的獨佔鎖
從 FileOutputStream 創建的 FileChannel 是可寫的。因此,我們可以獲得一個獨佔鎖:
try (FileOutputStream fileOutputStream = new FileOutputStream("/tmp/testfile.txt"); FileChannel channel = fileOutputStream.getChannel(); FileLock lock = channel.lock()) { }2. 使用 RandomAccessFile 的獨佔鎖
使用 RandomAccessFile,我們需要設置 [constructor](https://docs.oracle.com/javase/8/docs/api/java/io/RandomAccessFile.html#RandomAccessFile(java.io.File, java.lang.String)) 方法的第二個參數。
在這裡,我們將使用讀寫權限打開文件:
try (RandomAccessFile file = new RandomAccessFile("/tmp/testfile.txt", "rw"); FileChannel channel = file.getChannel(); FileLock lock = channel.lock()) { }
如果我們以只讀模式打開文件,並嘗試向其通道進行寫入操作,將會拋出 NonWritableChannelException 異常。
3.獨佔鎖依賴於可讀的 FileChannel如前所述,獨佔鎖需要一個可寫通道。因此,我們無法通過從 FileInputStream 創建的 FileChannel 獲得獨佔鎖:
try (RandomAccessFile file = new RandomAccessFile("/tmp/testfile.txt", "rw"); FileChannel channel = file.getChannel(); FileLock lock = channel.lock()) { }hannel.lock()) {
在上面的例子中,lock() 方法將拋出一個 nonwriteablechannelexception 。實際上,這是因為我們正在對一個創建只讀通道的 FileInputStream調用 getChannel。這個例子只是為了證明我們不能寫到一個不可寫的通道。事實上,我們不會捕捉並重新拋出異常。
# 共享鎖記住,共享鎖也稱為讀 鎖。因此,要獲得讀鎖,我們必須使用可讀的文件通道。
這樣的 FileChannel 可以通過調用 FileInputStream 或 RandomAccessFile 上的 getChannel() 方法獲得。同樣,另一個選項是使用 FileChannel 類的靜態 open 方法。在這種情況下,我們將第二個參數設置為 StandardOpenOption.READ 。
try (RandomAccessFile file = new RandomAccessFile("/tmp/testfile.txt", "rw"); FileChannel channel = file.getChannel(); FileLock lock = channel.lock()) { }
這裡要注意的一點是,我們選擇通過調用 lock(0, Long.MAX_VALUE, true) 來鎖定整個文件。通過將前兩個參數更改為不同的值,我們還可以只鎖定文件的特定區域。對於共享鎖,第三個參數必須設置為true。
為了簡單起見,我們將在下面的所有示例中鎖定整個文件,但請記住,我們始終可以鎖定文件的特定區域。
1. 使用 FileInputStream 中的共享鎖
從 FileInputStream 獲得的 FileChannel 是可讀的。因此,我們可以獲得一個共享鎖:try (FileInputStream fileInputStream = new FileInputStream("/tmp/testfile.txt"); FileChannel channel = fileInputStream.getChannel(); FileLock lock = channel.lock(0, Long.MAX_VALUE, true)) { }
在上面的代碼片段中,將成功調用通道上的 lock() 。這是因為共享鎖只要求通道是可讀的就行。
2. 使用 RandomAccessFile中的共享鎖
這次,我們只需要使用 」讀」 權限打開文件即可:try (RandomAccessFile file = new RandomAccessFile("/tmp/testfile.txt", "r"); FileChannel channel = file.getChannel(); FileLock lock = channel.lock(0, Long.MAX_VALUE, true)) { }
在本例中,我們創建了一個具有讀取權限的RandomAccessFile對象,然後從中創建一個可讀通道,從而創建一個共享鎖。
3. 共享鎖依賴於可讀的 FileChannel因此,我們無法通過從 FileOutputStream 創建的 FileChannel 獲取共享鎖:
Path path = Files.createTempFile("foo","txt");
try (FileOutputStream fis = new FileOutputStream(path.toFile()); FileLock lock = fis.getChannel().lock(0, Long.MAX_VALUE, true)) { } catch (NonWritableChannelException e) { }
在本例中,調用 lock() 嘗試獲取從 FileOutputStream 創建的通道上的共享鎖。這樣的通道是只寫的。它不能滿足通道必須可讀的需要。這將觸發一個NonWritableChannelException。
同樣,這段代碼只是為了證明我們不能從一個不可讀的通道中讀取。
# 思考實際上,使用文件鎖是困難的;鎖定機制是不可移植的。我們需要考慮到這一點來設計鎖定邏輯。
在POSIX系統中,鎖是建議性的。讀取或寫入給定文件的不同進程必須就鎖定協議達成一致。這將確保文件的完整性。作業系統本身不會強制任何鎖定。
在Windows上,除非允許共享,否則鎖將是獨佔的。討論作業系統特定機制的優點或缺點超出了本文的討論範圍。然而,在實現鎖定機制時,了解這些細微差別很重要。
# 總結在本教程中,我們回顧了在Java中獲取文件鎖的幾種不同選項。
首先,我們首先了解兩種主要的鎖定機制,以及Java NIO庫如何促進鎖定文件。
然後,我們瀏覽了一系列簡單的示例,這些示例顯示我們可以在應用程式中獲得獨佔和共享鎖。我們還研究了使用文件鎖時可能遇到的典型異常類型。