文件鎖可以是shared(共享鎖)或者exclusive(排他鎖)。不是所有的平臺都以同一種方式實(shí)現(xiàn)文件鎖姻几,不同的操作系統(tǒng)可能不同掸驱,同一操作系統(tǒng)上的不同文件系統(tǒng)也可能不同。有些操作系統(tǒng)只提供協(xié)同鎖饭豹,有些只提供強(qiáng)制鎖鸵赖,有些則都提供。
文件鎖是以文件為單位的拄衰,不是以通道它褪,也不是線程。所以文件鎖不適合同一個(gè)多個(gè)線程訪問的情形翘悉。如果一個(gè)線程獲得了給定文件的排他鎖茫打,第二個(gè)線程請求打開了一個(gè)新的channel,請求獲得排他鎖镐确,請求會被批準(zhǔn)包吝。但如果這兩個(gè)線程運(yùn)行在不同的JVM中,第二個(gè)線程會阻塞源葫,因?yàn)殒i往往是根據(jù)進(jìn)程來進(jìn)行裁決诗越,而不是線程。鎖工作于一個(gè)文件息堂,而不是單獨(dú)的文件處理器或是通道嚷狞。
/*
如果你需要控制多個(gè)線程之間的同步,你可能需要實(shí)現(xiàn)自己的輕量級的鎖荣堰,內(nèi)存映射文件可能是個(gè)適合的選擇
*/
public abstract class FileChannel extends AbstractChannel implements ByteChannel, GatheringByteChannel, ScatteringByteChannel {
public final FileLock lock()
public abstract FileLock lock (long position, long size, boolean shared)
public final FileLock tryLock()
public abstract FileLock tryLock(long position, long size, boolean shared)
}
先看帶參數(shù)的lock方法床未,獲得給定區(qū)域的鎖,自position開始振坚,size大小薇搁,第三個(gè)布爾參數(shù)代表是鎖是否共享。鎖的區(qū)域并不受到文件大小的限制渡八,鎖可以超過文件的大小啃洋,也就是說在一段區(qū)域被寫入數(shù)據(jù)之前鎖住,是可行的屎鳍。相反的宏娄,如果文件的大小超出了鎖的限制,也就將不受到鎖的限制逮壁。不帶參數(shù)的lock方法孵坚,等效于
fileChannel.lock(0L,Long.MAX_VALUE, false);
如果你的請求是有效的,那么lock方法就會生效,但是要等待前一個(gè)鎖(如果存在的話)釋放卖宠。
tryLock方法是lock方法非阻塞的變種巍杈,功能和lock相似,但是如果不能立刻獲得鎖的話逗堵,tryLock會返回null秉氧。從創(chuàng)建開始,直到調(diào)用FileLock的release方法蜒秤,F(xiàn)ileLock對象都是有效的汁咏。可以通過isValid方法測試作媚。一個(gè)鎖是否有效可能會改變攘滩,但鎖的位置,大小纸泡,是否共享漂问,是不變的。
你可以通過isShared判斷鎖是否為共享鎖女揭,如果內(nèi)在的文件系統(tǒng)操作系統(tǒng)不支持共享蚤假,那么這個(gè)方法總是會返回false,就算你傳遞true作為構(gòu)造函數(shù)也一樣吧兔。FileLock是線程安全的磷仰,多個(gè)線程可以通過一個(gè)FileLock進(jìn)行操作。盡管FileLock對象和一個(gè)Channel相關(guān)境蔼,但是其實(shí)鎖是和內(nèi)在的文件聯(lián)系的灶平。這有可能造成沖突,也有可能死鎖箍土,如果你完成了操作而沒有釋放鎖的話逢享。一個(gè)典型的代碼如下所示:
FileLock lock = fileChannel.lock();
try{
<perform read/write/whatever on channel>
} catch (IOException e) {
<handle unexcepted exception>
} finally {
lock.release();
}
下面是一個(gè)使用FileLock進(jìn)行操作的例子
private static final int SIZEOF_INT = 4;
private static final int INDEX_START = 0;
private static final int INDEX_COUNT = 10;
private static final int INDEX_SIZE = INDEX_COUNT * SIZEOF_INT;
private ByteBuffer buffer = ByteBuffer.allocate(INDEX_SIZE);
private IntBuffer indexBuffer = buffer.asIntBuffer();
private Random rand = new Random();
public static void main(String[] args) throws Exception{
boolean writer = false;
String filename;
//決定你所做的操作,讀或者寫
if(args.length!=2) {
System.out.println("Usage: [-r|-w] filename");
return;
}
writer = args[0].equals("-w");//true寫false讀
filename = args[1];
RandomAccessFile raf = new RandomAccessFile(filename,writer?"rw":"r");
FileChannel fc = raf.getChannel();//通過RandomAccessFile拿到fileChannel
LockTest lockTest = new LockTest();
if(writer) {
lockTest.doUpdates(fc);
} else {
lockTest.doQueries(fc);
}
}
void doQueries (FileChannel fc) throws Exception {
//如果是單次操作的話吴藻,沒有這個(gè)循環(huán)瞒爬,這里使用這個(gè)循環(huán),為了多次
//運(yùn)行程序沟堡,發(fā)現(xiàn)鎖的工作原理
while (true) {
FileLock lock = fc.lock(INDEX_START,INDEX_SIZE,true);
int reps = rand.nextInt(60) + 20;
for(int i=0; i<reps; i++) {
int n = rand.nextInt(INDEX_COUNT);
int position = INDEX_START + (n*SIZEOF_INT);
buffer.clear();
fc.read(buffer,position);
int value = indexBuffer.get(n);
Thread.sleep(100);//doing some work
}
lock.release();
Thread.sleep(rand.nextInt(3000)+500);
}
}
void doUpdates (FileChannel fc) throws Exception {
while (true) {
FileLock lock = fc.lock(INDEX_START,INDEX_SIZE,false);
updateIndex(fc);
lock.release();
Thread.sleep(rand.nextInt(2000)+500);
}
}
private int idxval = 1;
private void updateIndex (FileChannel fc) throws Exception{
indexBuffer.clear();
for(int i=0; i<INDEX_COUNT; i++) {
idxval++;
indexBuffer.put(idxval);
Thread.sleep(500);
}
buffer.clear();
fc.write(buffer,INDEX_START);
}
}