2015-05-21 45 views
4

我遇到了這種情況,不明白爲什麼會發生這種情況。有人可以幫我理解nio文件鎖定的行爲。在已鎖定的文件上打開文件輸出流會覆蓋該文件

我使用FileOutputStream打開了一個文件,並在使用nio FileLock獲得獨佔鎖之後,我在文件中寫入了一些數據。沒有釋放鎖。在同一個文件上打開另一個FileOutputStream,目的是獲取鎖並執行寫操作,並期望這會失敗。但是打開第二個文件輸出流會覆蓋已鎖定的文件,該文件在寫入數據之前甚至在我嘗試獲得第二個鎖之前。這是預期的嗎?我的理解是獲得排他鎖可以防止對鎖定文件進行任何更改。如何防止在嘗試獲取另一個鎖時覆蓋我的文件? (?彷彿另一個進程試圖獲得對同一文件鎖在不同的虛擬機)

示例程序我想:

 File fileToWrite = new File("C:\\temp\\myfile.txt"); 
     FileOutputStream fos1 = new FileOutputStream(fileToWrite); 
     FileOutputStream fos2 =null; 
     FileLock lock1,lock2 =null; 
     lock1=fos1.getChannel().tryLock(); 
     if(lock1!=null){ 
      //wrote date to myfile.txt after acquiring lock 
      fos1.write(data.getBytes()); 
      //opened myfile.txt again and this replaced the file 
      fos2 = new FileOutputStream(fileToWrite); 
      //got an overlappingfilelock exception here 
      lock2=fos2.getChannel().tryLock(); 
      fos2.write(newdata.getBytes()); 
      } 

     lock1.release(); 
     fos1.close(); 
     if(lock2!=null) 
      lock2.release(); 
     fos2.close(); 

也試過上述分成兩個節目。執行1st並從1st開始等待。被程序1鎖定的文件被程序2覆蓋。下面的示例:

PROGRAM1:

File fileToWrite = new File("C:\\temp\\myfile.txt"); 
    FileOutputStream fos1 = new FileOutputStream(fileToWrite); 
    FileLock lock1 =null; 
    lock1=fos1.getChannel().tryLock(); 
    if(lock1!=null){ 
     //wrote date to myfile.txt after acquiring lock 
     fos1.write(data.getBytes()); 
     System.out.println("wrote data and waiting"); 
     //start other program while sleep 
     Thread.sleep(10000); 
     System.out.println("finished wait"); 
     } 

    lock1.release(); 
    fos1.close(); 

Program2中:

File fileToWrite = new File("C:\\temp\\myfile.txt"); 
    System.out.println("opening 2nd out stream"); 
    //this overwrote the file 
    FileOutputStream fos2 = new FileOutputStream(fileToWrite); 
    FileLock lock2 =null; 
    lock2=fos2.getChannel().tryLock(); 
    //lock is null here 
    System.out.println("lock2="+lock2); 
    if(lock2!=null){ 
     //wrote date to myfile.txt after acquiring lock 
     System.out.println("writing NEW data"); 
     fos2.write(newdata.getBytes()); 
     } 

    if(lock2!=null) 
     lock2.release(); 
    fos2.close(); 

感謝

回答

1

當你獲得一種FileLock,你得到它爲整個JVM。這就是爲什麼在同一JVM中創建更多FileOutputStream並覆蓋同一文件將永遠不會被FileLock阻止--JVM擁有該鎖。因此,OverlappingFileLockException並不是要告訴你鎖不可用(這將通過tryLock通過返回null發信號通知),它的意思是告訴你有一個編程錯誤:嘗試獲取已經存在的鎖擁有。

當試圖從不同的JVM訪問相同的文件時,您偶然發現鎖定不一定會阻止其他進程寫入鎖定區域,這隻會阻止它們鎖定該區域。而且,由於您正在使用constructor which truncates existing files,所以在您嘗試獲取鎖之前可能會發生這種情況。

一個解決方案是使用new FileOutputStream(fileToWrite, true)來避免截斷文件。無論您是在相同的JVM還是不同的進程中打開文件,這都可以工作。

但是,也許你不想追加到文件。我想你想在你成功獲得鎖定的情況下覆蓋。在這種情況下,FileOutputStream的構造函數不會幫助您,因爲它們會強制您決定是截斷還是追加。

解決方案是放棄舊API和open the FileChannel directly(至少需要Java 7)。那麼你有很多standard open options其中truncatingappending是不同的。忽略這兩個允許覆蓋不急切地截斷文件:

try(FileChannel fch=FileChannel.open(fileToWrite.toPath(), 
            StandardOpenOption.CREATE, StandardOpenOption.WRITE)){ 
    try(FileLock lock=fch.tryLock()) { 
    if(lock!=null) { 
     // you can directly write into the channel 
     // but in case you really need an OutputStream: 
     OutputStream fos=Channels.newOutputStream(fch); 
     fos.write(testData.getBytes()); 
     // you may explicitly truncate the file to the actually written content: 
     fch.truncate(fch.position()); 
     System.out.println("waiting while holding lock..."); 
     LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(5)); 
    } 
    else System.out.println("couldn't acquire lock"); 
    } 
} 

由於它需要Java 7反正你可以使用自動資源管理清理。請注意,此代碼使用CREATE,這意味着如果不存在,則創建文件的已知行爲與不同,需要該文件不存在。

由於指定的選項,open操作可能會創建文件但不能截斷它。所有後續操作僅在獲取鎖定成功時執行。

+0

MKe你的想法。要麼鎖定所有其他用法,如覆蓋(第1段)*或*鎖定其他鎖定(第2段)。不是在同一時間。 – EJP

+0

@EJP:它*可能*被*其他*進程鎖定而不被覆蓋,但是,正如您所引用的「* system-dependent因此未指定*」。我所說的是它絕對*不*防止*相同* JVM覆蓋,即使在鎖定防止覆蓋的系統上。它*不會鎖定*不同*進程的其他鎖。 – Holger

1

文件鎖定只只指定對其他文件鎖工作。

Javadoc

Whether or not a lock actually prevents another program from accessing the content of the locked region is system-dependent and therefore unspecified. The native file-locking facilities of some systems are merely advisory, meaning that programs must cooperatively observe a known locking protocol in order to guarantee data integrity. On other systems native file locks are mandatory, meaning that if one program locks a region of a file then other programs are actually prevented from accessing that region in a way that would violate the lock. On yet other systems, whether native file locks are advisory or mandatory is configurable on a per-file basis. To ensure consistent and correct behavior across platforms, it is strongly recommended that the locks provided by this API be used as if they were advisory locks.

+0

但是在鎖定過程中,它不允許刪除文件。出現異常 - 「進程無法訪問該文件,因爲它正在被另一個進程使用。」 – Archana

+1

這是因爲你打開了它,而不是因爲鎖定。它只是Wimdows。 – EJP