jdk文档总说FileLock:
"File locks are held on behalf of the entire Java virtual machine. They are not suitable for controlling access to a file by multiple threads within the same virtual machine. "
"文件锁定以整个 Java 虚拟机来保持。但它们不适用于控制同一虚拟机内多个线程对文件的访问。"
表示了FileLock是面向整个虚拟机的,而不是面向线程的。我写了个测试类import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;public class FileLockTest {

public static void main(String[] args) throws Exception {
Thread t =new Thread(){
@Override
public void run() {
FileChannel channel;
try {
channel = new RandomAccessFile(new File("c:/menu.txt"), "rw").getChannel();
FileLock lock =channel.lock();
System.out.println("Thread1 拿到lock:"+lock+"  "+lock.isShared());
channel.position(channel.size());
Thread.sleep(1000*10);
for(int i =0;i<10;i++){
channel.write(ByteBuffer.wrap((i+" hello \n").getBytes()));
}
lock.release();
channel.close();
System.out.println("over1");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}
};
t.start();
Thread.sleep(500);

t =new Thread(){
@Override
public void run() {
System.out.println("Thread2 开始");
FileChannel channel;
try {
channel = new RandomAccessFile(new File("c:/menu.txt"), "rw").getChannel();
ByteBuffer bb=ByteBuffer.allocate(1024);
channel.read(bb);
bb.flip();
System.out.println(bb.asCharBuffer());
System.out.println("over2");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
t.start();
}

}
输出:Thread1 拿到lock:sun.nio.ch.FileLockImpl[0:9223372036854775807 exclusive valid]  false
Thread2 开始
java.io.IOException: 另一个程序已锁定文件的一部分,进程无法访问。
at sun.nio.ch.FileDispatcher.read0(Native Method)
at sun.nio.ch.FileDispatcher.read(Unknown Source)
at sun.nio.ch.IOUtil.readIntoNativeBuffer(Unknown Source)
at sun.nio.ch.IOUtil.read(Unknown Source)
at sun.nio.ch.FileChannelImpl.read(Unknown Source)
at nio.FileLockTest$2.run(FileLockTest.java:48)
显示出同一个进程里的2个线程也互斥了。到底FileLock是进程还是线程级别的,应该怎样正确的使用?
ps:我的操作系统是:windows xp

解决方案 »

  1.   

    文件锁
     
    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,我们需能对此做相应处理。以下是简单的销实例:
     
    Java代码  
    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.}  
      

  2.   

    FileLock是直接映射到底层的操作系统的锁定,所以进程间互斥,这点LZ应该是没问题吧
    LZ想问统一进程的多线程是否也互斥,对吧?
    在javadoc的FileLock里就有说明,文件锁有独占式和共享式,独占式则其他线程无法获取任意一种重叠锁,共享式是则其他线程可以获取重叠共享锁,而LZ程序中channel.lock();返回的是独占锁(可以看API文档),所以线程间也互斥。是否是共享锁,可以调用FileLock的isShared方法查看。
      

  3.   

    还要对楼上做个补充,共享锁是对读操作而言,即使是共享锁也只允许一个写;
    lz的问题的确是因为获得了默认的独占锁造成的,但是即使改成共享锁程序也不行
    改成共享锁后第一个线程会汇报IO异常,原因就在于共享锁也只是对读操作共享的,共享锁内控制的逻辑代码应该是读操作