文件鎖(Filelock)


文件鎖

    在解決Bug的過程中,遇到過這種問題:就是文件正在使用的過程,從文件管理器里面將文件刪除,這樣可能會導致一些不可預料的問題。在查閱了Java中File類的相關函數之后,在windows下面,可以使用File.rename()或 File.delete(),但是在Linux下面,這種方法也不行,文件還是被直接刪除了,發現也沒有什么很好的標記可以說明一個文件正在被使用中。最后,發現文件鎖(FileLock)可以給文件一個鎖,另一個程序在使用的時候判斷文件是否有文件鎖,就可以判斷出文件是否正在使用。但是這種方法,在卸載SD卡的時候,好像接收不到SD卡卸載廣播,不知道是什么原因,最后還是使用的Sharepreference,跨文件訪問。不過文件鎖也是一個比較實用的知識點,了解一下。

  JDK 1.4引入了文件加鎖機制,允許我們同步訪問一個共享文件,不過,競爭同一文件的兩個線程有可能在不同的java虛擬機上,或者一個是java線程,另一個是操作系統中其他的某個線程,但文件鎖對其他線程或其他操作系統進程都是可見的,因為java的文件加鎖直接映射到了本地操作系統的加鎖機制。

    注,這里講的鎖是指鎖定其他應用程序,而不是鎖定同一虛擬機里訪問的同一文件的其他線程 。如果在同一虛擬機兩次鎖定同一文件或某文件里的同一區域,tryLock與lock則會拋出OverlappingFileLockException異常。

    要想獲取整個文件的鎖,可以用FileChannel的tryLock( )或lock( )方法。(SocketChannel,DatagramChannel,以及 ServerSocketChannel是不需要鎖的,因為它們是從單進程實體繼承而來;一般來說,你是不會讓兩個進程去共享一個網絡socket的。tryLock( ) 是非阻塞的,它會試着去獲取這個鎖,但是如果得不到(其它進程已經以獨占方式得到這個鎖了),那它就直接返回;而lock( )是阻塞的,如果得不到鎖,它會在一直處於阻塞狀態,除非它得到了鎖,或者你打斷了調用它的線程,或者關閉了它要lock()的channel,否則它是不會返回的。最后用FileLock.release( )釋放鎖。
    還可以像這樣鎖住文件的某一部分
        tryLock(long position, long size, boolean shared)
    或者
        lock(long position, long size, boolean shared)
    這個方法能鎖住文件的某個區域(size – position)。其中第三個參數表示是否是共享鎖。

    雖然在修改文件的過程中,無參數的lock( )和tryLock( )方法的鎖定范圍會隨文件大小的變化,帶參數的方法卻不行。如果你鎖住了position到position+size這段范圍,而文件的長度又增加了,那么position+size后面是不加鎖的。而無參數的lock方法則會鎖定整個文件,不管它變不變長。

    鎖是獨占的還是共享的,這要由操作系統來決定。如果操作系統不支持共享鎖,而程序又申請了一個共享鎖,那么它會返回一個獨占鎖。你可以用FileLock.isShared( )來查詢鎖的類型(共享還是獨占)。

    在寫文件時才能鎖定,如果對一個只讀文件通道進行鎖定操作時,會拋NonWritableChannelException異常,即new FileInputStream(“data2.txt”).getChannel().tryLock();時就會拋異常。

    另外鎖定寫文件通道new FileOutputStream(“data2.txt”).getChannel().tryLock();時,它會清掉原文件中的內容,所以當文件中有內容時最好使用 new FileOutputStream(“data2.txt”,true).getChannel().tryLock(); 以追加方式打開寫文件通道。或者使用RandomAccessFile類來創建文件通道然后鎖定 new RandomAccessFile(“data2.txt”,”rw”).getChannel().tryLock(); ,這樣它不會破壞鎖定的文件的內容。

    最后在使用tryLock()獲取鎖時, 有可能獲取不到,這時就會為null,我們需能對此做相應處理。以下是簡單的銷實例:

  1. import java.io.FileOutputStream;  
  2. import java.nio.channels.FileLock;  
  3.   
  4. public class FileLocking {  
  5.     public static void main(String[] args) throws Exception {  
  6.         FileOutputStream fos = new FileOutputStream("file.txt");  
  7.         //獲取文件鎖 FileLock 對象  
  8.         FileLock fl = fos.getChannel().tryLock();  
  9.         //tryLock是嘗試獲取鎖,有可能為空,所以要判斷  
  10.         if (fl != null) {  
  11.             System.out.println("Locked File");  
  12.             Thread.sleep(100);  
  13.             fl.release();//釋放鎖  
  14.             System.out.println("Released Lock");  
  15.         }  
  16.         fos.close();  
  17.     }  
  18. }  

 

鎖定映射文件中的部分內容

    文件映射通常用於很大的文件,因此我們可能需要對文件操作的部分進行加鎖,以便其他進程可以修改文件中未被加鎖的部分。

 

  1. import java.io.IOException;
    import java.io.RandomAccessFile;
    import java.nio.ByteBuffer;
    import java.nio.MappedByteBuffer;
    import java.nio.channels.FileChannel;
    import java.nio.channels.FileLock;
    
    public class LockingMappedFiles {
    	static final int LENGTH = 0x200000; // 2 Mb
    	//static final int LENGTH = 100;
    	static FileChannel fc;
    
    	public static void main(String[] args) throws Exception {
    		//使用可隨機訪問文件創建可讀寫文件通道
    		fc = new RandomAccessFile("test.txt", "rw").getChannel();
    		//內存映射可讀寫文件,並映射至整個文件
    		MappedByteBuffer out = fc.map(FileChannel.MapMode.READ_WRITE, 0, LENGTH);
    		for (int i = 0; i < LENGTH; i++) {//寫滿2M內容
    			out.put((byte) 'x');
    		}
    		//鎖定前1/3內容
    		new LockAndModify(out, 0, 0 + LENGTH / 3);
    		//從文件中間開始鎖定1/4內容,注,要鎖定的內容一定不能有與
    		//已經鎖定的內容,否則拋OverlappingFileLockException
    		new LockAndModify(out, LENGTH / 2, LENGTH / 2 + LENGTH / 4);
    	}
    
    	private static class LockAndModify extends Thread {
    		private ByteBuffer buff;
    		private int start, end;
    
    		LockAndModify(ByteBuffer mbb, int start, int end) {
    			this.start = start;
    			this.end = end;
    
    			//調整可最大讀寫位置
    			mbb.limit(end);
    			//調整讀寫起始位置
    			mbb.position(start);
    			//創建新的子緩沖區,但與原緩沖是共享同一片數據,
    			//只是緩沖區位置、界限和標記值是相互獨立的
    			buff = mbb.slice();
    			start();
    		}
    
    		public void run() {
    			try {
    				// 獲取獨占鎖,如果要鎖定的部分被其他應用程序鎖定,則會阻塞,至到獲取鎖為止
    				FileLock fl = fc.lock(start, end, false);
    				System.out.println("Locked: " + start + " to " + end);
    				System.out.println(buff.position() + "  " + buff.limit());
    
    				// 進行修改操作,前當前位置類
    				while (buff.position() < buff.limit() - 1) {
    					buff.put((byte) (buff.get() + 1));
    				}
    				//JVM退出,或者channel關閉的時候會自動釋放這些鎖,但是你也可以用FileLock
    				//的release( )方法,明確地釋放鎖,就像這里釋放鎖一樣
    				fl.release();
    				System.out.println("Released: " + start + " to " + end);
    			} catch (IOException e) {
    				throw new RuntimeException(e);
    			}
    		}
    	}
    }

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM