• NIO文件锁FileLock


    目录

    linux文件锁flock

    NIO文件锁FileLock

    java程序怎么在一个电脑上只启动一次,只开一个进程

    文件锁可以是shared(共享锁)或者exclusive(排他锁)。不是所有的平台都以同一种方式实现文件锁,不同的操作系统可能不同,同一操作系统上的不同文件系统也可能不同。有些操作系统只提供协同锁,有些只提供强制锁,有些则都提供。linux锁见《linux文件锁flock》的详细说明。

    文件锁是以文件为单位的,不是以通道,也不是线程。所以文件锁不适合同一个多个线程访问的情形。如果一个线程获得了给定文件的排他锁,第二个线程请求打开了一个新的channel,请求获得排他锁,请求会被批准。但如果这两个线程运行在不同的JVM中,第二个线程会阻塞,因为锁往往是根据进程来进行裁决,而不是线程。锁工作于一个文件,而不是单独的文件处理器或是通道。

    如果你需要控制多个线程之间的同步,你可能需要实现自己的轻量级的锁,内存映射文件可能是个适合的选择。

    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)
    }

    先看带参数的lock方法,获得给定区域的锁,自position开始,size大小,第三个布尔参数代表是锁是否共享。锁的区域并不受到文件大小的限制,锁可以超过文件的大小,也就是说在一段区域被写入数据之前锁住,是可行的。相反的,如果文件的大小超出了锁的限制,也就将不受到锁的限制。不带参数的lock方法,等效于
    fileChannel.lock(0L,Long.MAX_VALUE, false);
    如果你的请求是有效的,那么lock方法就会生效,但是要等待前一个锁(如果存在的话)释放。

    tryLock方法是lock方法非阻塞的变种,功能和lock相似,但是如果不能立刻获得锁的话,tryLock会返回null。从创建开始,直到调用FileLock的release方法,FileLock对象都是有效的。可以通过isValid方法测试。一个锁是否有效可能会改变,但锁的位置,大小,是否共享,是不变的。

    你可以通过isShared判断锁是否为共享锁,如果内在的文件系统操作系统不支持共享,那么这个方法总是会返回false,就算你传递true作为构造函数也一样。FileLock是线程安全的,多个线程可以通过一个FileLock进行操作。尽管FileLock对象和一个Channel相关,但是其实锁是和内在的文件联系的。这有可能造成冲突,也有可能死锁,如果你完成了操作而没有释放锁的话。一个典型的代码如下所示:

    FileLock lock = fileChannel.lock();
    try{
      <perform read/write/whatever on channel>
    } catch (IOException e) {
      <handle unexcepted exception>
    } finally {
      lock.release();
    }

    下面是一个使用FileLock进行操作的例子

    package com.dxz.nettydemo.websocket;
    
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.RandomAccessFile;
    import java.nio.ByteBuffer;
    import java.nio.channels.FileChannel;
    import java.nio.channels.FileLock;
    import java.util.Date;
    
    public class FileLockTest {
    
        public static void main(String[] args) {
            FileChannel channel = null;
            FileLock lock = null;
            try {
                // 1. 对于一个只读文件通过任意方式加锁时会报NonWritableChannelException异常
                // 2. 无参lock()默认为独占锁,不会报NonReadableChannelException异常,因为独占就是为了写
                // 3. 有参lock()为共享锁,所谓的共享也只能读共享,写是独占的,共享锁控制的代码只能是读操作,当有写冲突时会报NonWritableChannelException异常
                channel = new FileOutputStream("d:\temp\logfile.txt", true).getChannel();
                RandomAccessFile raf = new RandomAccessFile("d:\temp\logfile.txt", "rw");
    
                // 在文件末尾追加内容的处理
                raf.seek(raf.length());
                channel = raf.getChannel();
    
                // 获得锁方法一:lock(),阻塞的方法,当文件锁不可用时,当前进程会被挂起
                //lock = channel.lock();// 无参lock()为独占锁
                // lock = channel.lock(0L, Long.MAX_VALUE, true);//有参lock()为共享锁,有写操作会报异常
    
                // 获得锁方法二:trylock(),非阻塞的方法,当文件锁不可用时,tryLock()会得到null值
                do {
                    lock = channel.tryLock();
                    System.out.println(Thread.currentThread().getName() + "get lock = " + lock);
                } while (null == lock);
    
                // 互斥操作
                ByteBuffer sendBuffer = ByteBuffer.wrap((new Date() + " 写入
    ").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();
                    }
                }
            }
            
            try {
                Thread.sleep(50000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

    多启动几个,观察跨进程间的文件锁作用了。

    看看获取lock和释放lock的源码:FileChannelImpl.java

    http://www.docjar.com/html/api/sun/nio/ch/FileChannelImpl.java.html

          public FileLock tryLock(long position, long size, boolean shared)
     1008           throws IOException
     1009       {
     1010           ensureOpen();
     1011           if (shared && !readable)
     1012               throw new NonReadableChannelException();
     1013           if (!shared && !writable)
     1014               throw new NonWritableChannelException();
     1015           FileLockImpl fli = new FileLockImpl(this, position, size, shared);
     1016           FileLockTable flt = fileLockTable();
     1017           flt.add(fli);
     1018           int result;
     1019   
     1020           int ti = threads.add();
     1021           try {
     1022               try {
     1023                   ensureOpen();
     1024                   result = nd.lock(fd, false, position, size, shared);
     1025               } catch (IOException e) {
     1026                   flt.remove(fli);
     1027                   throw e;
     1028               }
     1029               if (result == FileDispatcher.NO_LOCK) {
     1030                   flt.remove(fli);
     1031                   return null;
     1032               }
     1033               if (result == FileDispatcher.RET_EX_LOCK) {
     1034                   assert shared;
     1035                   FileLockImpl fli2 = new FileLockImpl(this, position, size,
     1036                                                        false);
     1037                   flt.replace(fli, fli2);
     1038                   return fli2;
     1039               }
     1040               return fli;
     1041           } finally {
     1042               threads.remove(ti);
     1043           }
     1044       }
     1045   
     1046       void release(FileLockImpl fli) throws IOException {
     1047           int ti = threads.add();
     1048           try {
     1049               ensureOpen();
     1050               nd.release(fd, fli.position(), fli.size());
     1051           } finally {
     1052               threads.remove(ti);
     1053           }
     1054           assert fileLockTable != null;
     1055           fileLockTable.remove(fli);
     1056       }
  • 相关阅读:
    关于SQL批量插入数据方法比较
    Meta详细说明及使用方法
    【原创】自己写的用户控件的传值
    Windows 2003全面优化
    IT职位全面解析(软件类)
    NHibernate介绍
    获取到的客户端发送的文件的MIME内容类型的全部类型列
    C#如何编程方式获取计算机主板序列号
    XP下HTTP的403.9错误禁止访问:连接的用户过多如何解
    用户登录验证程序——VB.NET
  • 原文地址:https://www.cnblogs.com/duanxz/p/6782789.html
Copyright © 2020-2023  润新知