黑马程序员_O‘Reilly java nio学习笔记之通道_文件通道

来源:互联网 发布:淘宝店铺红酒psd素材 编辑:程序博客网 时间:2024/05/23 22:18

---------------------- android培训、java培训、期待与您交流! ----------------------


3..文件通道

文件通道总是阻塞式的,FileChannel对象是线程安全(thread-safe)的。

3.1   访问文件

关于访问文件的API

abstract  long position()    返回此通道的文件位置。 

abstract  FileChannel position(long newPosition)    设置此通道的文件位置。如果尝试将通  

     道position设置为一个负值,会导致llegalArgumentException异常,不过可以把position 设  

     置到超出文件尾,这样做会把position设置为指定值而不改变文件大小。假如在将position 设  

     置为超出当前文件大小时实现了一个read( )方法,那么会返回一个文件尾条件;倘若此时实 

     现的是一个write( )方法则会引起文件增长以容纳写入的字节并可能导致出现一个文件空洞 

     (file hole,即文件逻辑上不含数据的区域)。  

abstract  int read(ByteBuffer dst)   将字节序列从此通道读入给定的缓冲区。 

long read(ByteBuffer[] dsts)    将字节序列从此通道读入给定的缓冲区。 

abstract  long read(ByteBuffer[] dsts, int offset, int length) 

          将字节序列从此通道读入给定缓冲区的子序列中。 

abstract  int read(ByteBuffer dst, long position) 

          从给定的文件位置开始,从此通道读取字节序列,并写入给定的缓冲区。 

abstract  long size()     返回此通道的文件的当前大小。 

abstract  FileChannel truncate(long size)    将此通道的文件截取为给定大小。 

abstract  int write(ByteBuffer src)     将字节序列从给定的缓冲区写入此通道。 

long write(ByteBuffer[] srcs)    将字节序列从给定的缓冲区写入此通道。 

abstract  long write(ByteBuffer[] srcs, int offset, int length) 

          将字节序列从给定缓冲区的子序列写入此通道。 

abstract  int write(ByteBuffer src, long position) 

          从给定的文件位置开始,将字节序列从给定缓冲区写入此通道。 

abstract  void force(boolean metaData)  强制将对此通道的文件更新写入该文件的存储设备中。 

          参数表示在方法返回值前文件的元数据(metadata)是否也要被同步更新到磁盘。 该信

          息对数据恢复而言不重要。给force传递 false 值表示只需要同步文件数据的更改。该  

          方法对于关键操作如事务(transaction)处理来说,是非常重要的,可以保证数据完整

          性和可靠的恢复。

    当字节被以上read或write方法传输时,文件position会自动更新。如果position值达到了文件大小的值(文件大小的值可以通过size( )方法返回),read方法会返回一个文件尾条件值(-1)。可是,不同于缓冲区的是,如果实现write( )方法时position前进到超过文件大小的值,该文件会扩展以容纳新写入的字节。但是,position 参数的绝对形式的read( )和write( )方法不会更新位置信息,此时多个线程可以并发访问同一个文件不会相互产生干扰。尝试在文件末尾之外的position进行一个绝对读操作,size( )方法会返回一个end-of-file

    FileChannel位置(position)是从底层的文件描述符获得的,该position 同时被作为通道引用获取来源的文件对象。这也就意味着一个对象对该position的更新可以被另一个对象看到,如: 

RandomAccessFile randomAccessFile = new RandomAccessFile ("filename", "r"); 

randomAccessFile.seek (1000); 

FileChannel fileChannel = randomAccessFile.getChannel( ); 

System.out.println ("file pos: " + fileChannel.position( )); // This will print "1000" 

randomAccessFile.seek (500); 

System.out.println ("file pos: " + fileChannel.position( )); // This will print "500" 

fileChannel.position (200); 

System.out.println ("file pos: " + randomAccessFile.getFilePointer( )); //200

    当需要减少一个文件的size时,truncate方法会砍掉您所指定的新size值之外的所有数据。如果当前size大于新 size,超出新 size的所有字节都会被悄悄地丢弃。如果提供的新size值大于或

等于当前的文件size值,该文件不会被修改。这两种情况下,truncate都会产生副作用:文件的position 会被设置为所提供的新size值。

3.2   锁定文件

文件锁定的方法有:

FileLock lock()   获取对此通道的文件的独占锁定,阻塞方法,如果文件已经被锁定,会阻塞至

                  锁的释放。等价于:fileChannel.lock (0L, Long.MAX_VALUE, false);  

abstract  FileLock lock(long position, long size, boolean shared) 

          获取此通道的文件给定区域上的锁定。方法指定文件内部锁定区域的开始position 以及

          锁定区域的size。第三个参数  shared 表示您想获取的锁是共享的(参数值为true)还

          是独占的(参数值为false)。要获得一个共享锁,您必须先以只读权限打开文件,而请

          求独占锁时则需要写权限,您提供的position和 size 参数的值不能是负数。锁定区域

          的范围可以扩展从而超出文件尾。因此,我们可以提前把待写入数据的区域锁定,我们

          也可以锁定一个不包含任何文件内容的区域,比如文件最后一个字节以外的区域。如果

          之后文件增长到达那块区域,那么您的文件锁就可以保护该区域的文件内容了。相反地,

          如果您锁定了文件的某一块区域,然后文件增长超出了那块区域,那么新增加的文件内

          容将不会受到您的文件锁的保护。   

FileLock tryLock() 和abstract  FileLock tryLock(long position, long size, boolean shared)以上两个方法的非阻塞变体,不过如果请求的锁不能立即获取到则会返回一个null。 

          

锁(lock)可以是共享的(shared)或独占的(exclusive)。本节中描述的文件锁定特性在很大程度上依赖本地的操作系统实现。并非所有的操作系统和文件系统都支持共享文件锁。对于那些不支持的,对一个共享锁的请求会被自动提升为对独占锁的请求。重要注意:锁的对象是文件而不是通道或线程,这意味着文件锁不适用于判优同一台Java虚拟机上的多个线程发起的访问。如果一个线程在某个文件上获得了一个独占锁,然后第二个线程利用一个单独打开的通道来请求该文件的独占锁,那么第二个线程的请求会被批准。但如果这两个线程运行在不同的Java虚拟 机上,那么第二个线程会阻塞,因为锁最终是由操作系统或文件系统来判优的并且几乎总是在进程级而非线程级上判优。锁都是与一个文件关联的,而不是与单个的文件句柄或通道关联。 

文件锁详解:

   文件锁,即FileLock类,封装一个锁定的文件区域。FileLock对象是线程安全的,多个线程可以并发访问一个锁对象。常用方法有:

FileChannel channel()  返回文件通道,此锁定保持在该通道的文件上。 

boolean isShared()  测试一个锁以判断它是共享的还是独占的。如果底层的操作系统或文件系统

        不支持共享锁,那么该方法将总是返回false 值,即使您申请锁时传递的参数值是 true。   

        假如您的程序依赖共享锁定行为,请测试返回的锁以确保您得到了您申请的锁类型。

abstract  boolean isValid()    判断此锁定是否有效。 

boolean overlaps(long position, long size)    查询一个FileLock对象是否与一个指定的文件

        区域重叠。这可以使您迅速判断您拥有的锁是否与一个感兴趣的区域有交叉。不过,即使  

        返回值是false 也不能保证您就一定能在期望的区域上获得一个锁,因为Java虚拟机上的

        其他地方或者外部进程可能已经在该期望区域上有一个或多个锁了。最好使用tryLock( )    

        方法确认一下。 

long position()            返回文件内锁定区域中第一个字节的位置。 

abstract  void release()   释放此锁定。 

long size()                返回锁定区域的大小,以字节为单位。 

String toString()          返回描述此锁定的范围、类型和有效性的字符串。 

如果您在使用完一个锁后而不释放它的话,可能会导致冲突或者死锁,因此,释放锁的语句应当放在finally代码块中。

以下示例输入两个参数,一个为读取文件模式,第二个为文件名称(文件需存在)。

public class LockTest {

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");

filename = args[1];

RandomAccessFile raf = new RandomAccessFile(filename, (writer) ? "rw"

"r");

FileChannel fc = raf.getChannel();

LockTest lockTest = new LockTest();

if (writer) {

lockTest.doUpdates(fc);

else {

lockTest.doQueries(fc);

}

}

void doQueries(FileChannel fc) throws Exception {

while (true) {

System.out.println("trying for shared lock...");

FileLock lock = fc.lock(INDEX_STARTINDEX_SIZEtrue);

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

System.out.println("Index entry " + n + "=" + value);

Thread.sleep(100);

}

lock.release();

System.out.println("<sleeping>");

Thread.sleep(rand.nextInt(3000) + 500);

}

}

void doUpdates(FileChannel fc) throws Exception {

while (true) {

System.out.println("trying for exclusive lock...");

FileLock lock = fc.lock(INDEX_STARTINDEX_SIZEfalse);

updateIndex(fc);

lock.release();

System.out.println("<sleeping>");

Thread.sleep(rand.nextInt(2000) + 500);

}

}

private void updateIndex(FileChannel fc) throws Exception {

int id = 1;

indexBuffer.clear();

for (int i = 0; i < INDEX_COUNT; i++) {

id++;

System.out.println("Updating index " + i + "=" + id);

indexBuffer.put(id);

Thread.sleep(500);

}

buffer.clear();

fc.write(bufferINDEX_START);

}

}




---------------------- android培训、java培训、期待与您交流! ----------------------

详细请查看:http://edu.csdn.net/heima

原创粉丝点击