第一种方法:使用RandomAccessFile类操作文件。
在java.io.RandomAccessFile类的open方法,提供了参数实现独占的方式打开文件:
RandomAccessFile raf = new RandomAccessFile(file, "rws");
其中的“rws”参数,rw代表读取和写入,s代表了同步方式,也就是同步锁。这种方式打开的文件,就是独占方式的。
第二种方法:使用sun.nio.FileChannel对文件进行加锁。
代码:
RandomAccessFile raf = new RandomAccessFile("file.txt", "rw"); FileChannel fc = raf.getChannel(); FileLock fl = fc.tryLock(); if(fl.isValid()) System.out.println("You have got the file lock."); 以上是通过RandomAccessFile来获得文件锁的,那么在写文件的时候如何对文件加锁呢?方法如下: 代码: FileOutputStream fos = new FileOutputStream("file.txt"); FileChannel fc = fos.getChannel(); //获取FileChannel对象 FileLock fl = fc.tryLock(); //or fc.lock(); if(null != fl) System.out.println("You have got file lock."); //TODO write content to file //TODO write end, should release this lock fl.release(); //释放文件锁 fos.close; //关闭文件写操作 如果在读文件操作的时候,对文件进行加锁,怎么操作呢?从API文档中我们可以看到, FileChannel也可以从FileInputStream中直接获得,但是这种直接获得FileChannel的对象直接去 操作FileLock会报异常NonWritableChannelException,这样我们又怎么去获得文件锁呢? 需要自己去实现getChannel方法,代码如下: private static FileChannel getChannel(FileInputStream fin, FileDescriptor fd) { FileChannel channel = null; synchronized(fin){ channel = FileChannelImpl.open(fd, true, true, fin); return channel; } }
其实,我们看FileInputStream时,发现getChannel方法与我们写的代码只有一个地方不同,即open方法的第三个参数不同,如果设置为false,就不能锁住文件了。缺省的getChannel方法,就是false,因此,不能锁住文件。
通过对 FileChannel 调用 tryLock() 或 lock() ,就可以获得整个文件的 FileLock
Trylock 与 lock 方法
tryLock() 是非阻塞式的,它设法获取锁,但如果不能获得,例如因为其他一些进程已经持有相同的锁,而且不共享时,它将直接从方法调用返回。
lock() 是阻塞式的,它要阻塞进程直到锁可以获得,或调用 lock() 的线程中断,或调用 lock() 的通道关闭。
对独占锁和共享锁的支持必须由底层的操作系统提供。锁的类型可以通过 FileLock.isShared() 进行查询。另外,我们不能获取缓冲器上的锁,只能是通道上的。
共享锁 与 独占锁 区别
独占锁 :也称排它锁,如果一个线程获得一个文件的独占锁,那么其它线程就不能再获得同一文件的独占锁或共享锁,直到独占锁被释放。
共享锁 :如果一个线程获得一个文件的共享锁,那么其它线程可以获得同一文件的共享锁或同一文件部分内容的共享锁,但不能获取排它锁
当 a.txt 文件被加独占锁时 其他线程不可读也不可写
当 a.txt 文件被加共享锁时 其他线程可读也不可写
如何获得共享锁
fc.tryLock(position,size,isShare); 第三个参数为 true 时 为共享锁
例:
import java.io.*; import java.net.InetAddress; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.channels.FileLock; import java.nio.channels.OverlappingFileLockException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class TestServlet extends HttpServlet { protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { File f = new File("F://kankan//opt.txt"); RandomAccessFile fi = new RandomAccessFile(f, "rw"); FileChannel fc = fi.getChannel(); FileLock fl = null; Thread t = Thread.currentThread(); System.out.println(t.getId() + " " + t.getName()); InetAddress address = InetAddress.getLocalHost(); try { try { fl = fc.tryLock(); if (fl == null) { System.out.println(address.getHostAddress() + " wait 5 sec for reason (null)!"); throw new OverlappingFileLockException(); } } catch (OverlappingFileLockException e) { System.out.println(address.getHostAddress() + " wait 5 sec once!"); try { Thread.sleep(5 * 1000); } catch (InterruptedException e1) { e1.printStackTrace(); } try { fl = fc.tryLock(); if (fl == null) { System.out.println(address.getHostAddress() + " wait 5 sec for reason (null)!"); throw new OverlappingFileLockException(); } } catch (OverlappingFileLockException oe) { System.out.println(address.getHostAddress()+ " cannot get file lock!"); throw new OverlappingFileLockException(); } } if (fl != null) { System.out.println(address.getHostAddress() + " start write!"); fc.position(fc.size()); fc.write(ByteBuffer .wrap((address.getHostAddress() + " =============1=============== /r/n") .getBytes())); System.out.println(address.getHostAddress() + " stop write!"); } } catch (Exception e) { System.out.println(address.getHostAddress()+ " exception or cannot get file lock!"); response.getWriter().write(address.getHostAddress() + " exception or cannot get file lock!"); return; } finally { if (null != fl && fl.isValid()) { fl.release(); System.out.println(address.getHostAddress() + " release filelock"); } fc.close(); fi.close(); } } }