1.文件鎖的定義
FileLock是文件鎖,進程鎖,用於進程間並發,控制不同程序(JVM)對同一文件的並發訪問。
FileLock是java 1.4 版本后出現的一個類,它可以通過對一個可寫文件(w)加鎖,保證同時只有一個進程可以拿到文件的鎖,這個進程從而可以對文件做訪問;而其它拿不到鎖的進程要么選擇被掛起等待,要么選擇去做一些其它的事情,這樣的機制保證了眾進程可以順序訪問該文件。
可以看出,能夠利用文件鎖的這種性質,在一些場景下,雖然我們不需要向文件中寫入數據,不受其他程序的打擾,文件鎖就很合適。
java.nio.channels.FileChannel,FileChannel是NIO中的一個類。
2.獨占鎖與共享鎖
獨占鎖:也稱排它鎖,如果一個線程獲得一個文件的獨占鎖,那么其它線程就不能再獲得同一文件的獨占鎖或共享鎖,直到獨占鎖被釋放。
共享鎖:如果一個線程獲得一個文件的共享鎖,那么其它線程可以獲得同一文件的共享鎖或同一文件部分內容的共享鎖,但不能獲取排它鎖
當a.txt文件被加獨占鎖時 其他線程不可讀也不可寫。
當a.txt文件被加共享鎖時 其他線程可讀也不可寫。
fc.tryLock(position,size,isShare);第三個參數為true時 為共享鎖。
所以一個進程中的線程獲得了文件鎖,希望進程內其他線程可以做讀操作時,可以使用共享鎖。寫操作是不被支持的。
3.使用tryLock來獲取鎖
lock()阻塞的方法,鎖定范圍可以隨着文件的增大而增加。
tryLock()非阻塞,當未獲得鎖時,返回null。
FileLock的生命周期:調用FileLock.release()或者Channel.close(),或者JVM關閉。
boolean java.nio.channels.FileLock.overlaps(long position, long size),true表示當前鎖在區域內,false表示當前鎖的區域與參數區域不重疊。
文件鎖的效果是與操作系統相關的,是由操作系統底層來實現的。比如,在windows下,進程間不能同時讀寫一個文件,而在Linux下,不同的進程可以同時讀寫一個文件。
在讀寫關鍵數據時加鎖,操作完成后解鎖,lock.release();放到finally中。
一次性申請所有需要的資源,並且在申請不成功的情況下放棄已申請到的資源;
public class FileLockTest { /** * 如代碼所示,需要進行互斥的進程只要將自己的代碼替換掉//互斥操作即可, * 每個進程在運行實際邏輯功能代碼之前,會嘗試獲取鎖文件鎖, * 得到文件鎖的進程可以繼續執行后續的代碼,而沒有獲得鎖文件的進程將被操作系統掛起(suspend), * 等到其它進程將文件鎖釋放后再重新開始嘗試獲取文件鎖。 * 這樣子,進程就可以通過FileLock來實現間的互斥運行。 * @param args */ public static void main(String[] args){ FileChannel channel = null; FileLock lock = null; try { // 對於一個只讀文件通過任意方式加鎖時會報NonWritableChannelException異常 // 同樣對寫通道通過有參lock()方式加鎖時也會報NonReadableChannelException異常 // 無參lock()默認為獨占鎖,不會報NonReadableChannelException異常,因為獨占就是為了寫 // 所謂的共享也只能讀共享,寫是獨占的,共享鎖控制的代碼只能是讀操作 // channel = new FileOutputStream("logfile.txt",true).getChannel(); RandomAccessFile raf = new RandomAccessFile("logfile.txt","rw"); raf.seek(raf.length());//raf在文件末尾追加內容的處理 channel = raf.getChannel(); // 獲得鎖方法一lock,阻塞的方法,當文件鎖不可用時,當前進程會被掛起 // lock = channel.lock(0L, Long.MAX_VALUE, true);//共享鎖,有寫操作會報異常 lock = channel.lock();//獨占鎖 // 獲得鎖方法二trylock,非阻塞的方法,當文件鎖不可用時,tryLock()會得到null值 // do { // lock = channel.tryLock(); // } while(null == lock); // 互斥操作 ByteBuffer sendBuffer=ByteBuffer.wrap((new Date()+" 寫入\n").getBytes()); channel.write(sendBuffer); Thread.sleep(5000); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } finally { if(lock != null) { try { lock.release(); lock = null; } catch (IOException e) { e.printStackTrace(); } } if(channel != null) { try { channel.close(); channel = null; } catch (IOException e) { e.printStackTrace(); } } } } } /** * 間隔一秒鍾兩次運行本程序,程序會在文件鎖的控制下對logfile.txt進行互斥操作 logfile.txt內容: Thu Aug 16 15:39:02 CST 2012 寫入 Thu Aug 16 15:39:07 CST 2012 寫入 當采用第二種方法時,若還未獲得文件鎖就對文件進行操作,則會報以下異常: Exception in thread "main" java.io.IOException: 另一個程序已鎖定文件的一部分,進程無法訪問。 at sun.nio.ch.FileDispatcher.write0(Native Method) at sun.nio.ch.FileDispatcher.write(Unknown Source) at sun.nio.ch.IOUtil.writeFromNativeBuffer(Unknown Source) at sun.nio.ch.IOUtil.write(Unknown Source) at sun.nio.ch.FileChannelImpl.write(Unknown Source) at FileLockTest.main(FileLockTest.java:19) */