文件通道解析二(文件锁,关闭通道)
程序员文章站
2022-07-13 17:00:30
...
文件通道解析一(读写操作,通道数据传输等):http://donald-draper.iteye.com/blog/2374603
引言:
上一篇文章看了文件通道,读写操作,通道数据传输操作,先来回顾一下:
文件通道的构造,主要是初始化通道读写模式,追加模式append及文件分发器,FileDispatcherImpl。
文件通道的读写操作的实际操作都是由IOUtil协助FileDispatcherImpl完成,这一点和SocketChannel通道读写思路基本相同。
文件通道传输方法transferTo,首先确保当前文件通道是否打开,是否可读,然后检查目的通道是否关闭,是否可写;然后先调用文件通道本地方法传输通道的数据到目的通道,如果失败,则将文件通道数据,映射到内存MappedByteBuffer,然后调用目的通道的写操作(MappedByteBuffer),如果再失败,则将通道数据,写到DirectByteBuffer中,然后在调用目的通道的写操作(DirectByteBuffer)。
文件通道传输方法transferFrom,确保当前通道可写,打开,源通道可读打开,如果源通道为文件通道,将源通道数据映射的内存MappedByteBuffer,然后由IOUtil协助FileDispatcherImpl,将MappedByteBuffer
写入当前通道,如果源通道非文件通道,则先调用源通道的读操作,从源通道读取数据,写到临时DirectByteBuffer,委托write,写DirectByteBuffer到当前通道,即由IOUtil协助FileDispatcherImpl,将DirectByteBuffer写入当前通道。
在往下看之前我们先把文件通道的相关filed贴出,以便我们理解:
现在我们来文件的lock和trylock方法
再来看锁文件region方法:
//尝试加锁
从lock方法和try方法来看,首先检查共享模式参数shared与当前通道的读写模式是否匹配,然后根具postion,size和shared信息构造文件锁FileLockImpl,添加到通道文件锁表filelocktable中,再通过文件分发器FileDispatcherImpl锁文件region,如果成功,则创建新的文件锁替换旧的文件锁。如果lock失败,则从通道文件锁表filelocktable移除先前添加的文件锁。这里为什么要先创建文件锁添加到通道文件锁表filelocktable中,可能是为了先抢占通道文件锁表的位置,再去通FileDispatcherImpl锁文件region,成功则创建新的文件锁替换旧的文件锁。lock方法和trylock方法不同的是在FileDispatcherImpl锁文件region这个过程,lock为循环文件region,直到成功,而trylock方法,只锁一次,成功则则创建新的文件锁替换旧的文件锁,失败则从通道文件锁表filelocktable移除先前添加的文件锁,返回。
lock方法和trylock方法我们几点要关注:
1.
2.
3.
下面分别来看这几点:
1.
//FileLockImpl
//FileLock
2.
从上面可以看出如果为通道文件锁表为共享模式,则创建SharedFileLockTable,否则为
SimpleFileLockTable;
上述方法中break MISSING_BLOCK_LABEL_73类似的语句,这就由于反编译错误导致的,break语句有两种,一种为平时我们用的无label的break,即在for或while,switch语句中直接break;,break还有一种方式为break Label;即跳出循环到指定的Label:
实例:
由于反编译插件无法识别这样的语句,所以出现break MISSING_BLOCK_LABEL_73类似的语句。
Branching Statements-The break Statement:http://docs.oracle.com/javase/tutorial/java/nutsandbolts/branch.html
Dealing with labels in decompiled code:http://*.com/questions/6347930/dealing-with-labels-in-decompiled-code
我们先看一下FileLockTable的定义
再来看SimpleFileLockTable
//FileChannelImpl-SimpleFileLockTable
再来看SharedFileLockTable
SharedFileLockTable用于存储文件共享锁,SimpleFileLockTable用于存储文件互质锁;
两者最大的不同是SimpleFileLockTable用list去管理文件锁,而SharedFileLockTable
用于ConcurrentHashMap管理文件锁。
3.
//FileDispatcherImpl
来看释放文件锁
来看这一句:
//FileDispatcherImpl
//获取文件锁table共享模式
再来看关闭通道
//FileDispatcherImpl
总结:
lock方法和try方法,首先检查共享模式参数shared与当前通道的读写模式是否匹配,然后根据具postion,size和shared信息构造文件锁FileLockImpl,添加到通道文件锁表filelocktable中,再通过文件分发器FileDispatcherImpl锁文件region,如果成功,则创建新的文件锁替换旧的文件锁。如果lock失败,则从通道文件锁表filelocktable移除先前添加的文件锁。这里为什么要先创建文件锁添加到通道文件锁表filelocktable中,可能是为了先抢占通道文件锁表的位置,再去通FileDispatcherImpl锁文件region,成功则创建新的文件锁替换旧的文件锁。lock方法和trylock方法不同的是在FileDispatcherImpl锁文件region这个过程,lock为循环文件region,直到成功,而trylock方法,只锁一次,成功则则创建新的文件锁替换旧的文件锁,失败则从通道文件锁表filelocktable移除先前添加的文件锁,返回。
附:
引言:
上一篇文章看了文件通道,读写操作,通道数据传输操作,先来回顾一下:
文件通道的构造,主要是初始化通道读写模式,追加模式append及文件分发器,FileDispatcherImpl。
文件通道的读写操作的实际操作都是由IOUtil协助FileDispatcherImpl完成,这一点和SocketChannel通道读写思路基本相同。
文件通道传输方法transferTo,首先确保当前文件通道是否打开,是否可读,然后检查目的通道是否关闭,是否可写;然后先调用文件通道本地方法传输通道的数据到目的通道,如果失败,则将文件通道数据,映射到内存MappedByteBuffer,然后调用目的通道的写操作(MappedByteBuffer),如果再失败,则将通道数据,写到DirectByteBuffer中,然后在调用目的通道的写操作(DirectByteBuffer)。
文件通道传输方法transferFrom,确保当前通道可写,打开,源通道可读打开,如果源通道为文件通道,将源通道数据映射的内存MappedByteBuffer,然后由IOUtil协助FileDispatcherImpl,将MappedByteBuffer
写入当前通道,如果源通道非文件通道,则先调用源通道的读操作,从源通道读取数据,写到临时DirectByteBuffer,委托write,写DirectByteBuffer到当前通道,即由IOUtil协助FileDispatcherImpl,将DirectByteBuffer写入当前通道。
在往下看之前我们先把文件通道的相关filed贴出,以便我们理解:
public class FileChannelImpl extends FileChannel { private static final long allocationGranularity = initIDs(); private final FileDispatcher nd;//文件分发器 private final FileDescriptor fd;//文件描述 private final boolean writable;//通道是否可写 private final boolean readable;//通道是否可读 private final boolean append;//通道写是否为追加模式 private final Object parent;//创建通道的对象 //下面这些属性,暂时不能确定是具体什么意思,只能先从字面上去理解, //这里我们先放在这里,后面用到在讲 private final NativeThreadSet threads = new NativeThreadSet(2); private final Object positionLock = new Object();//文件读写是位置锁 private static volatile boolean transferSupported = true;//是否支持通道传输 private static volatile boolean pipeSupported = true;//是否支持管道 private static volatile boolean fileSupported = true;//是否支持文件 private static final long MAPPED_TRANSFER_SIZE = 8388608L;/ private static final int TRANSFER_SIZE = 8192; private static final int MAP_RO = 0; private static final int MAP_RW = 1; private static final int MAP_PV = 2; private volatile FileLockTable fileLockTable;//存放文件锁的Table private static boolean isSharedFileLockTable;//文件锁table是否为共享 private static volatile boolean propertyChecked; static final boolean $assertionsDisabled = !sun/nio/ch/FileChannelImpl.desiredAssertionStatus(); static { //加载nio,net资源库 Util.load(); } private static native long initIDs(); }
现在我们来文件的lock和trylock方法
再来看锁文件region方法:
public FileLock lock(long l, long l1, boolean flag) throws IOException { FileLockImpl filelockimpl; FileLockTable filelocktable; boolean flag1; int i; ensureOpen(); if(flag && !readable)//如果写模式,锁不能为共享模式 throw new NonReadableChannelException(); if(!flag && !writable)//如果为读模,则锁必须共享模式 throw new NonWritableChannelException(); //创建文件锁 filelockimpl = new FileLockImpl(this, l, l1, flag); //获取文件通道的文件锁table filelocktable = fileLockTable(); //将文件锁,加入通道文件锁table中 filelocktable.add(filelockimpl); flag1 = false; i = -1; FileLock filelock; begin(); ... int j; //不断的尝试加锁,直到成功 do //尝试锁文件region j = nd.lock(fd, true, l, l1, flag); while(j == 2 && isOpen()); if(isOpen()) { if(j == 1) { if(!$assertionsDisabled && !flag) throw new AssertionError(); //创建新的文件锁 FileLockImpl filelockimpl1 = new FileLockImpl(this, l, l1, false); //替换filelocktable旧的文件锁 filelocktable.replace(filelockimpl, filelockimpl1); filelockimpl = filelockimpl1; } flag1 = true; } 如果锁文件region失败,则从filelocktable移除 if(!flag1) filelocktable.remove(filelockimpl); threads.remove(i); try { end(flag1); } catch(ClosedByInterruptException closedbyinterruptexception) { throw new FileLockInterruptionException(); } ... return filelockimpl; }
//尝试加锁
public FileLock tryLock(long l, long l1, boolean flag) throws IOException { FileLockImpl filelockimpl; FileLockTable filelocktable; int j; ensureOpen(); if(flag && !readable)//如果写模式,锁不能为共享模式 throw new NonReadableChannelException(); if(!flag && !writable)//如果为读模,则锁必须共享模式 throw new NonWritableChannelException(); //创建文件锁 filelockimpl = new FileLockImpl(this, l, l1, flag); filelocktable = fileLockTable();//获取文件通道的文件锁table filelocktable.add(filelockimpl);//将文件锁,加入通道文件锁table中 j = threads.add(); int i; Object obj; try { ensureOpen(); //尝试锁文件region i = nd.lock(fd, false, l, l1, flag); } catch(IOException ioexception) { filelocktable.remove(filelockimpl); throw ioexception; } ... //创建新的文件锁 obj = new FileLockImpl(this, l, l1, false); //替换filelocktable旧的文件锁 filelocktable.replace(filelockimpl, ((FileLock) (obj))); filelockimpl1 = ((FileLockImpl) (obj)); threads.remove(j); return filelockimpl1; ... }
从lock方法和try方法来看,首先检查共享模式参数shared与当前通道的读写模式是否匹配,然后根具postion,size和shared信息构造文件锁FileLockImpl,添加到通道文件锁表filelocktable中,再通过文件分发器FileDispatcherImpl锁文件region,如果成功,则创建新的文件锁替换旧的文件锁。如果lock失败,则从通道文件锁表filelocktable移除先前添加的文件锁。这里为什么要先创建文件锁添加到通道文件锁表filelocktable中,可能是为了先抢占通道文件锁表的位置,再去通FileDispatcherImpl锁文件region,成功则创建新的文件锁替换旧的文件锁。lock方法和trylock方法不同的是在FileDispatcherImpl锁文件region这个过程,lock为循环文件region,直到成功,而trylock方法,只锁一次,成功则则创建新的文件锁替换旧的文件锁,失败则从通道文件锁表filelocktable移除先前添加的文件锁,返回。
lock方法和trylock方法我们几点要关注:
1.
//创建文件锁 filelockimpl = new FileLockImpl(this, l, l1, flag);
2.
filelocktable = fileLockTable();//获取文件通道的文件锁table
3.
j = nd.lock(fd, true, l, l1, flag);
下面分别来看这几点:
1.
//创建文件锁 filelockimpl = new FileLockImpl(this, l, l1, flag);
//FileLockImpl
public class FileLockImpl extends FileLock { private volatile boolean valid;//有效性标志 static final boolean $assertionsDisabled = !sun/nio/ch/FileLockImpl.desiredAssertionStatus(); //构造 FileLockImpl(FileChannel filechannel, long l, long l1, boolean flag) { super(filechannel, l, l1, flag); valid = true;//初始化有效 } FileLockImpl(AsynchronousFileChannel asynchronousfilechannel, long l, long l1, boolean flag) { super(asynchronousfilechannel, l, l1, flag); valid = true; } //判断文件锁是否有效 public boolean isValid() { return valid; } //使文件锁无效 void invalidate() { //如果断言开启,断言线程是否持有锁 if(!$assertionsDisabled && !Thread.holdsLock(this)) { throw new AssertionError(); } else { valid = false; return; } } //释放文件锁 public synchronized void release() throws IOException { //获取锁关联的通道 Channel channel = acquiredBy(); if(!channel.isOpen())//通道关闭 throw new ClosedChannelException(); if(valid)//在通道有效的情况下 { if(channel instanceof FileChannelImpl) //将实际工作委托相应的通道 ((FileChannelImpl)channel).release(this); else if(channel instanceof AsynchronousFileChannelImpl) ((AsynchronousFileChannelImpl)channel).release(this); else throw new AssertionError(); valid = false; } } }
//FileLock
public abstract class FileLock implements AutoCloseable { private final Channel channel;//锁关联通道 private final long position;//文件锁region的起始位置 private final long size;//region大小 private final boolean shared;//是否为共享模式 //构造文件锁 protected FileLock(FileChannel channel, long position, long size, boolean shared) { if (position < 0) throw new IllegalArgumentException("Negative position"); if (size < 0) throw new IllegalArgumentException("Negative size"); if (position + size < 0) throw new IllegalArgumentException("Negative position + size"); this.channel = channel; this.position = position; this.size = size; this.shared = shared; } /** * Returns the file channel upon whose file this lock was acquired. *返回用有本文件锁的通道 * This method has been superseded by the {@link #acquiredBy acquiredBy} * method. * * @return The file channel, or {@code null} if the file lock was not * acquired by a file channel. */ public final FileChannel channel() { return (channel instanceof FileChannel) ? (FileChannel)channel : null; } /** * Returns the channel upon whose file this lock was acquired. *//获取锁关联通道 * @return The channel upon whose file this lock was acquired. * * @since 1.7 */ public Channel acquiredBy() { return channel; } /** * Returns the position within the file of the first byte of the locked * region. *获取锁文件region的起始position * A locked region need not be contained within, or even overlap, the * actual underlying file, so the value returned by this method may exceed * the file's current size. * * @return The position */ public final long position() { return position; } /** * Returns the size of the locked region in bytes. *获取锁文件region的size * A locked region need not be contained within, or even overlap, the * actual underlying file, so the value returned by this method may exceed * the file's current size. * * @return The size of the locked region */ public final long size() { return size; } /** * Tells whether this lock is shared. *判断锁是否为共享模式 * @return <tt>true</tt> if lock is shared, * <tt>false</tt> if it is exclusive */ public final boolean isShared() { return shared; } /** * Tells whether or not this lock overlaps the given lock range. </p> *//判断加锁的文件region是否重叠 * @return <tt>true</tt> if, and only if, this lock and the given lock * range overlap by at least one byte */ public final boolean overlaps(long position, long size) { if (position + size <= this.position) return false; // That is below this if (this.position + this.size <= position) return false; // This is below that return true; } /** * Tells whether or not this lock is valid. *判断锁是否有效 * A lock object remains valid until it is released or the associated * file channel is closed, whichever comes first. * * @return <tt>true</tt> if, and only if, this lock is valid */ public abstract boolean isValid(); /** * Releases this lock. * * If this lock object is valid then invoking this method releases the * lock and renders the object invalid. If this lock object is invalid * then invoking this method has no effect. *释放锁 * @throws ClosedChannelException * If the channel that was used to acquire this lock * is no longer open * * @throws IOException * If an I/O error occurs */ public abstract void release() throws IOException; /** * This method invokes the {@link #release} method. It was added * to the class so that it could be used in conjunction with the * automatic resource management block construct. *关闭文件锁 * @since 1.7 */ public final void close() throws IOException { release(); } }
2.
filelocktable = fileLockTable();//获取文件通道的文件锁table
//获取文件通道的文件锁table private FileLockTable fileLockTable() throws IOException { if(fileLockTable != null) break MISSING_BLOCK_LABEL_96; FileChannelImpl filechannelimpl = this; JVM INSTR monitorenter ; int i; if(fileLockTable != null) break MISSING_BLOCK_LABEL_84; if(!isSharedFileLockTable()) break MISSING_BLOCK_LABEL_73; i = threads.add(); ensureOpen(); //如果为共享模式 fileLockTable = FileLockTable.newSharedFileLockTable(this, fd); threads.remove(i); break MISSING_BLOCK_LABEL_84; Exception exception; exception; threads.remove(i); throw exception; //否则 fileLockTable = new SimpleFileLockTable(); break MISSING_BLOCK_LABEL_96; Exception exception1; exception1; throw exception1; return fileLockTable; }
从上面可以看出如果为通道文件锁表为共享模式,则创建SharedFileLockTable,否则为
SimpleFileLockTable;
上述方法中break MISSING_BLOCK_LABEL_73类似的语句,这就由于反编译错误导致的,break语句有两种,一种为平时我们用的无label的break,即在for或while,switch语句中直接break;,break还有一种方式为break Label;即跳出循环到指定的Label:
实例:
testLable: for (obj : list){ if (flag) break testLable; }
由于反编译插件无法识别这样的语句,所以出现break MISSING_BLOCK_LABEL_73类似的语句。
Branching Statements-The break Statement:http://docs.oracle.com/javase/tutorial/java/nutsandbolts/branch.html
Dealing with labels in decompiled code:http://*.com/questions/6347930/dealing-with-labels-in-decompiled-code
我们先看一下FileLockTable的定义
abstract class FileLockTable { protected FileLockTable() { } //创建共享文件锁表 public static FileLockTable newSharedFileLockTable(Channel channel, FileDescriptor filedescriptor) throws IOException { return new SharedFileLockTable(channel, filedescriptor); } public abstract void add(FileLock filelock)//添加 throws OverlappingFileLockException; public abstract void remove(FileLock filelock);//移除 public abstract List removeAll();//移除所有 public abstract void replace(FileLock filelock, FileLock filelock1);//替换 }
再来看SimpleFileLockTable
//FileChannelImpl-SimpleFileLockTable
private static class SimpleFileLockTable extends FileLockTable { private final List lockList = new ArrayList(2);//文件锁集合 static final boolean $assertionsDisabled = !sun/nio/ch/FileChannelImpl.desiredAssertionStatus(); public SimpleFileLockTable() { } //添加文件锁 public void add(FileLock filelock) throws OverlappingFileLockException { synchronized(lockList) { checkList(filelock.position(), filelock.size()); lockList.add(filelock); } } //查看添加的文件锁,是否与文件锁集合中,已经加锁的文件region重叠 private void checkList(long l, long l1) throws OverlappingFileLockException { //如果断言开启,当前线程不只有文件锁集合,则抛出AssertionError if(!$assertionsDisabled && !Thread.holdsLock(lockList)) throw new AssertionError(); //遍历当前文件锁集合,查看添加的文件锁,是否与已经加锁的文件region重叠, //重叠则抛出OverlappingFileLockException for(Iterator iterator = lockList.iterator(); iterator.hasNext();) { FileLock filelock = (FileLock)iterator.next(); if(filelock.overlaps(l, l1)) throw new OverlappingFileLockException(); } } //移除文件锁 public void remove(FileLock filelock) { synchronized(lockList) { lockList.remove(filelock); } } //移除所有文件锁 public List removeAll() { List list = lockList; JVM INSTR monitorenter ; ArrayList arraylist; arraylist = new ArrayList(lockList); lockList.clear();//直接clear文件锁集合 return arraylist; Exception exception; exception; throw exception; } //替换文件锁,先移除filelock,后添加filelock1 public void replace(FileLock filelock, FileLock filelock1) { synchronized(lockList) { lockList.remove(filelock); lockList.add(filelock1); } } }
再来看SharedFileLockTable
class SharedFileLockTable extends FileLockTable { private static ConcurrentHashMap lockMap = new ConcurrentHashMap();//文件锁Map private static ReferenceQueue queue = new ReferenceQueue(); private final Channel channel;//关联通道 private final FileKey fileKey;//文件锁key static final boolean $assertionsDisabled = !sun/nio/ch/SharedFileLockTable.desiredAssertionStatus(); //文件锁引用 private static class FileLockReference extends WeakReference { FileKey fileKey() { return fileKey; } private FileKey fileKey; FileLockReference(FileLock filelock, ReferenceQueue referencequeue, FileKey filekey) { super(filelock, referencequeue); fileKey = filekey; } } //构造共享文件锁table SharedFileLockTable(Channel channel1, FileDescriptor filedescriptor) throws IOException { channel = channel1; //创建文件key fileKey = FileKey.create(filedescriptor); } //添加文件锁 public void add(FileLock filelock) throws OverlappingFileLockException { //从文件锁Map获取,文件key对应的锁集合 Object obj = (List)lockMap.get(fileKey); _L3: List list; label0: { if(obj != null) break MISSING_BLOCK_LABEL_95; //文件锁集合为null,则创建共享锁集合 obj = new ArrayList(2); synchronized(obj) { //将文件key与文件锁集合放入Map中 list = (List)lockMap.putIfAbsent(fileKey, obj); if(list != null) break label0; //将文件锁索引添加到锁集合中 ((List) (obj)).add(new FileLockReference(filelock, queue, fileKey)); } break; /* Loop/switch isn't completed */ } obj2; JVM INSTR monitorexit ; goto _L1 exception; throw exception; _L1: obj = list; List list1; label1: { synchronized(obj) { list1 = (List)lockMap.get(fileKey); if(obj != list1) break label1; checkList(((List) (obj)), filelock.position(), filelock.size()); ((List) (obj)).add(new FileLockReference(filelock, queue, fileKey)); } break; /* Loop/switch isn't completed */ } obj = list1; obj1; JVM INSTR monitorexit ; if(true) goto _L3; else goto _L2 exception1; throw exception1; _L2: removeStaleEntries(); return; } //如果文件key对应的,文件锁集合为空,则从map中移除文件key Entry private void removeKeyIfEmpty(FileKey filekey, List list) { if(!$assertionsDisabled && !Thread.holdsLock(list)) throw new AssertionError(); if(!$assertionsDisabled && lockMap.get(filekey) != list) throw new AssertionError(); if(list.isEmpty()) lockMap.remove(filekey); } //移除文件锁 public void remove(FileLock filelock) { if(!$assertionsDisabled && filelock == null) throw new AssertionError(); //从锁Map中获取文件key对应的集合 List list = (List)lockMap.get(fileKey); if(list == null) return; synchronized(list) { int i = 0; do { if(i >= list.size()) break; //获取文件锁引用 FileLockReference filelockreference = (FileLockReference)list.get(i); //参文件锁引用获取文件锁 FileLock filelock1 = (FileLock)filelockreference.get(); if(filelock1 == filelock) { if(!$assertionsDisabled && (filelock1 == null || filelock1.acquiredBy() != channel)) throw new AssertionError(); //找到,则清除引用,help GC filelockreference.clear(); list.remove(i);//从文件锁集合中移除 break; } i++; } while(true); } } //移除所有文件锁 public List removeAll() { ArrayList arraylist = new ArrayList(); //获取文件key的文件锁集合 List list = (List)lockMap.get(fileKey); if(list != null) synchronized(list) { //遍历锁集合,将通读文件的文件锁添加到arraylist for(int i = 0; i < list.size();) { FileLockReference filelockreference = (FileLockReference)list.get(i); FileLock filelock = (FileLock)filelockreference.get(); if(filelock != null && filelock.acquiredBy() == channel) { filelockreference.clear(); list.remove(i); arraylist.add(filelock); } else { i++; } } //移除文件key的锁集合 removeKeyIfEmpty(fileKey, list); } return arraylist; } //替换文件锁 public void replace(FileLock filelock, FileLock filelock1) { //获取文件锁集合 List list = (List)lockMap.get(fileKey); if(!$assertionsDisabled && list == null) throw new AssertionError(); synchronized(list) { int i = 0; do { if(i >= list.size()) break; FileLockReference filelockreference = (FileLockReference)list.get(i); FileLock filelock2 = (FileLock)filelockreference.get(); if(filelock2 == filelock) { //找到对应的文件锁,则清除引用,help gc filelockreference.clear(); list.set(i, new FileLockReference(filelock1, queue, fileKey));//替换文件锁引用 break; } i++; } while(true); } } //检查文件key的锁集中的文件锁,锁住文件的region是否重叠 private void checkList(List list, long l, long l1) throws OverlappingFileLockException { if(!$assertionsDisabled && !Thread.holdsLock(list)) throw new AssertionError(); for(Iterator iterator = list.iterator(); iterator.hasNext();) { FileLockReference filelockreference = (FileLockReference)iterator.next(); FileLock filelock = (FileLock)filelockreference.get(); if(filelock != null && filelock.overlaps(l, l1)) throw new OverlappingFileLockException(); } } //清空文件锁Map private void removeStaleEntries() { do { FileLockReference filelockreference; //从引用队列中取出文件锁引用 if((filelockreference = (FileLockReference)queue.poll()) == null) break; //从文件锁引用获取文件key FileKey filekey = filelockreference.fileKey(); //获取文件key对应的文件锁 List list = (List)lockMap.get(filekey); if(list != null) synchronized(list) { list.remove(filelockreference); //移除文件key对应的文件锁集合 removeKeyIfEmpty(filekey, list); } } while(true); } }
SharedFileLockTable用于存储文件共享锁,SimpleFileLockTable用于存储文件互质锁;
两者最大的不同是SimpleFileLockTable用list去管理文件锁,而SharedFileLockTable
用于ConcurrentHashMap管理文件锁。
3.
j = nd.lock(fd, true, l, l1, flag);
//FileDispatcherImpl
int lock(FileDescriptor filedescriptor, boolean flag, long l, long l1, boolean flag1) throws IOException { return lock0(filedescriptor, flag, l, l1, flag1); } static native int lock0(FileDescriptor filedescriptor, boolean flag, long l, long l1, boolean flag1) throws IOException;
来看释放文件锁
void release(FileLockImpl filelockimpl) throws IOException { int i = threads.add(); ensureOpen(); //文件分发器先释放锁住的文件region nd.release(fd, filelockimpl.position(), filelockimpl.size()); ... if(!$assertionsDisabled && fileLockTable == null) { throw new AssertionError(); } else { //从文件锁table中移除文件锁 fileLockTable.remove(filelockimpl); return; } }
来看这一句:
//文件分发器先释放锁住的文件region nd.release(fd, filelockimpl.position(), filelockimpl.size());
//FileDispatcherImpl
void release(FileDescriptor filedescriptor, long l, long l1) throws IOException { release0(filedescriptor, l, l1); } static native void release0(FileDescriptor filedescriptor, long l, long l1) throws IOException
//获取文件锁table共享模式
private static boolean isSharedFileLockTable() { if(!propertyChecked) synchronized(sun/nio/ch/FileChannelImpl) { if(!propertyChecked) { String s = (String)AccessController.doPrivileged(new GetPropertyAction("sun.nio.ch.disableSystemWideOverlappingFileLockCheck")); isSharedFileLockTable = s == null || s.equals("false"); propertyChecked = true; } } return isSharedFileLockTable; }
再来看关闭通道
protected void implCloseChannel() throws IOException { if(fileLockTable != null) { //清空文件锁table for(Iterator iterator = fileLockTable.removeAll().iterator(); iterator.hasNext();) { FileLock filelock = (FileLock)iterator.next(); synchronized(filelock) { if(filelock.isValid()) { //释放文件锁,锁住的文件region nd.release(fd, filelock.position(), filelock.size()); ((FileLockImpl)filelock).invalidate();//使文件锁无效 } } } } //预先关闭文件描述 nd.preClose(fd); threads.signalAndWait(); //如果父对象不为null,则关闭父对象,否则关闭文件分发器 if(parent != null) ((Closeable)parent).close(); else nd.close(fd); }
//FileDispatcherImpl
void close(FileDescriptor filedescriptor) throws IOException { close0(filedescriptor); } static native void close0(FileDescriptor filedescriptor) throws IOException;
总结:
lock方法和try方法,首先检查共享模式参数shared与当前通道的读写模式是否匹配,然后根据具postion,size和shared信息构造文件锁FileLockImpl,添加到通道文件锁表filelocktable中,再通过文件分发器FileDispatcherImpl锁文件region,如果成功,则创建新的文件锁替换旧的文件锁。如果lock失败,则从通道文件锁表filelocktable移除先前添加的文件锁。这里为什么要先创建文件锁添加到通道文件锁表filelocktable中,可能是为了先抢占通道文件锁表的位置,再去通FileDispatcherImpl锁文件region,成功则创建新的文件锁替换旧的文件锁。lock方法和trylock方法不同的是在FileDispatcherImpl锁文件region这个过程,lock为循环文件region,直到成功,而trylock方法,只锁一次,成功则则创建新的文件锁替换旧的文件锁,失败则从通道文件锁表filelocktable移除先前添加的文件锁,返回。
附:
public class FileKey { private static native void initIDs(); private long dwVolumeSerialNumber; private long nFileIndexHigh; private long nFileIndexLow; static { initIDs(); } private FileKey() { } public static FileKey create(FileDescriptor filedescriptor) { FileKey filekey = new FileKey(); try { filekey.init(filedescriptor); } catch(IOException ioexception) { throw new Error(ioexception); } return filekey; } private native void init(FileDescriptor filedescriptor) throws IOException; public int hashCode() { return (int)(dwVolumeSerialNumber ^ dwVolumeSerialNumber >>> 32) + (int)(nFileIndexHigh ^ nFileIndexHigh >>> 32) + (int)(nFileIndexLow ^ nFileIndexHigh >>> 32); } public boolean equals(Object obj) { if(obj == this) return true; if(!(obj instanceof FileKey)) return false; FileKey filekey = (FileKey)obj; return dwVolumeSerialNumber == filekey.dwVolumeSerialNumber && nFileIndexHigh == filekey.nFileIndexHigh && nFileIndexLow == filekey.nFileIndexLow; } }
上一篇: Java NIO 系列教程
下一篇: NIO基本(3)