AsynchronousFileChannel类的使用
AsynchronousFileChannel类用于读取、写人和操作文件的异步通道。
在通过调用此类定义的open()方法打开文件时,将创建一个异步文件通道。该文件包含可读写的、可查询其当前大小的可变长度的字节序列。当写人字节超出其当前大小时,文件的大小会增加。文件的大小在截断时会减小。
异步文件通道在文件中没有当前位置,而是将文件位置指定给启动异步操作的每个读取和写人方法。
CompletionHandler 被指定为参数,并被调用以消耗I/O操作的结果。此类还定义了启动异步操作的读取和写人方法,并返回Future对象以表示操作的挂起结果。将来可用于检查操作是否已完成,等待完成,然后检索结果。
除了读写操作之外,此类还定义了一下操作:
1. 对文件所做的更新可能会被强制到底层存储设备,以确保在发生系统崩溃不会丢失数据
2. 文件的某个区域可能被其他程序的访问锁定
AsynchronousFileChannel与一个线程池关联,任务被提交来处理I/O事件,并发送到使用通道上I/O操作结果的CompletionHandler对象。在通道上启动的I/O操作的CompletionHandler保证由线程池中的一个线程调用(这样可以确保CompletionHandler程序由具有预期标识的线程运行)。如果I/O操作立即完成,并且起始线程本身是线程池中的线程,则启动线程可以直接调用完成处理程序。当创建AsynchronousFileChannel而不指定线程池时,该通道将与系统相关的默认线程池关联,该线程池可能与其他通道共享。默认线程池由AsynchronousChannelGroup类定义的系统属性配置。
此类型的通道可以安全地由多个并发线程使用。可以在任何时候调用close()方法,如通道接口所指定的那样。这将导致通道上的所有未完成的异步操作都使用异常AsynchronousCloseException。多个读写操作在同一时间可能是未完成的。当多个读写操作未完成时,将不指定I/O操作的顺序以及调用CompletionHandler程序的顺序。特别是,它们没有保证按照行动的启动顺序执行。读取或写人时使用的ByteBuffers不安全,无法由多个并发I/O操作使用。此外,在启动I/O操作之后,应注意确保在操作完成后才能访问缓冲区。
与FileChannel一样,此类的实例提供的文件的视图保证与同一程序中其他实例提供的同一文件的其他视图一致。但是,该类的实例提供的视图可能与其他并发运行的程序所看到的视图一致,也可能不一致,这是由于底层操作系统所执行的缓存和网络文件系统协议引起的延迟。无论编写这些程序的语言是什么,也无论它们是在同一台机器上运行还是在其他机器上,都是如此。任何此类不一致的确切性质都依赖于系统,因此未指定。
获取此通道文件的独占锁
public final Future<FileLock> lock()
方法的作用是获取此通道文件的独占锁。此方法启动一个操作以获取此通道的文件的独占锁。该方法返回一个表示操作的挂起结果的Future对象。Future的get()方法在成功完成时返回FileLock。调用此方法的行为及调用的方式与代码ch.lock(OL,Long.MAX__VALUE,false)
完全相同。返回值表示待定结果的Future.对象。
//测试类A
public static void main(String[] args) throws ExecutionException, InterruptedException, IOException {
Path path = Paths.get("a.txt");
AsynchronousFileChannel channel = AsynchronousFileChannel.open(path, StandardOpenOption.WRITE);
Future<FileLock> future = channel.lock();
FileLock lock = future.get();
System.out.println("A get lock time=" + System.currentTimeMillis());
//给出一些事件,用来启动另一个类
Thread.sleep(8000);
lock.release();
System.out.println("A release lock time=" + System.currentTimeMillis());
channel.close();
}
//测试类B
public static void main(String[] args) throws ExecutionException, InterruptedException, IOException {
Path path = Paths.get("a.txt");
AsynchronousFileChannel channel = AsynchronousFileChannel.open(path, StandardOpenOption.WRITE);
System.out.println("lock begin " + System.currentTimeMillis());
Future<FileLock> future = channel.lock();
System.out.println("lock end " + System.currentTimeMillis());
FileLock lock = future.get();
System.out.println("B get lock time=" + System.currentTimeMillis());
lock.release();
channel.close();
channel.close();
}
先运行A再运行B:
A get lock time=1591806128921
A release lock time=1591806136923
//A输出
//B输出
lock begin 1591806130471
lock end 1591806130475
B get lock time=1591806136924//A释放锁后B才获得
获取通道文件给定区域的锁
public abstract Future< Firelock>lock( long position, long size, boolean shared
方法的作用是获取此通道文件给定区域的锁。此方法启动一个操作以获取此信道文件的给定区域的
锁。
该方法的行为与lock(long,long, boolean, Object, Completion handler)
方法完全相同,不
同之处在于,此方法不指定 Completion Handler程序,而是返回一个表示待定结果的 Future
对象。
Future的get0方法在成功完成时返回 Firelock,参数 position代表锁定区域的起始位置,必须是非负数。size代表锁定区域的大小,必须是非负数,并且 position+size的结果必须是非负数。 shared值为tue代表请求的是共享锁,在这种情况下,此通道必须为读取(并可能写入)打开,如果请求排他锁,在这种情况下,此通道必须为写入而打开(并且可能读取)。返回值代表待定结果的 Future对象。
//TestA
public static void main(String[] args) throws IOException, ExecutionException, InterruptedException {
Path path = Paths.get("a.txt");
AsynchronousFileChannel channel = AsynchronousFileChannel.open(path, StandardOpenOption.WRITE);
Future<FileLock> future = channel.lock(0, 3, false);
FileLock lock = future.get();
System.out.println("A get lock time=" + System.currentTimeMillis());
Thread.sleep(8000);//给出事件启动TestB
lock.release();
System.out.println("A release lock time=" + System.currentTimeMillis());
channel.close();
}
//TestB
public static void main(String[] args) throws Exception{
Path path = Paths.get("a.txt");
AsynchronousFileChannel channel = AsynchronousFileChannel.open(path, StandardOpenOption.WRITE);
System.out.println("B lock begin=" + System.currentTimeMillis());
Future<FileLock> future = channel.lock(0, 3, false);
System.out.println("B lock end=" + System.currentTimeMillis());
FileLock lock = future.get();
System.out.println("B get lock time=" + System.currentTimeMillis());
lock.release();
channel.close();
}
//TestC
public static void main(String[] args) throws Exception{
Path path = Paths.get("a.txt");
AsynchronousFileChannel channel = AsynchronousFileChannel.open(path, StandardOpenOption.WRITE);
System.out.println("c lock begin " + System.currentTimeMillis());
Future<FileLock> future = channel.lock(4, 4, false);
System.out.println("c lock end " + System.currentTimeMillis());
FileLock lock = future.get();
System.out.println("c get lock time=" + System.currentTimeMillis());
lock.release();
channel.close();
}
先运行A再运行B:
//A
A get lock time=1591844912927
A release lock time=1591844920930
//B
B lock begin=1591844915954
B lock end=1591844915957
B get lock time=1591844920930//A释放B才获取
先运行A再运行C:
//A
A get lock time=1591844962242
A release lock time=1591844970245
//C
c lock begin 1591844964512
c lock end 1591844964515
c get lock time=1591844964517//A和C锁定的区域不同,并不阻塞
实现重叠锁定
在两个进程对同一个文件的锁定范围有重叠时,会出现阻塞的状态。
//A
public static void main(String[] args) throws Exception{
Path path = Paths.get("a.txt");
AsynchronousFileChannel channel = AsynchronousFileChannel.open(path, StandardOpenOption.WRITE);
Future<FileLock> future = channel.lock(0, 3, false);
FileLock lock = future.get();
System.out.println("A get lock time=" + System.currentTimeMillis());
Thread.sleep(8000);
lock.release();
System.out.println("A release lock time=" + System.currentTimeMillis());
channel.close();
}
//B
public static void main(String[] args) throws Exception{
Path path = Paths.get("a.txt");
AsynchronousFileChannel channel = AsynchronousFileChannel.open(path, StandardOpenOption.WRITE);
System.out.println("lock begin " + System.currentTimeMillis());
Future<FileLock> future = channel.lock(1, 5, false);
System.out.println("lock end " + System.currentTimeMillis());
FileLock lock = future.get();
System.out.println("B get lock time=" + System.currentTimeMillis());
lock.release();
channel.close();
}
//A
A get lock time=1591845350042
A release lock time=1591845358046
//B
lock begin 1591845351759
lock end 1591845351761
B get lock time=1591845358046
返回此通道文件当前大小与通道打开状态
public abstract long size()
方法的作用是返回此通道文件的当前大小。
public boolean isOpen()
方法的作用是判断通道是否呈打开的状态。
public static void main(String[] args) throws Exception{
Path path = Paths.get("a.txt");
AsynchronousFileChannel channel = AsynchronousFileChannel.open(path, StandardOpenOption.WRITE);
System.out.println("File size=" + channel.size());
System.out.println("A isOpen=" + channel.isOpen());
channel.close();
System.out.println("B isOpen=" + channel.isOpen());
}
File size=100
A isOpen=true
B isOpen=false
CompletionHandler接口的使用
public final <A> void lock(Aattachment,CompletionHandler<FileLock,? super A> handler)
方法的作用是获取此通道文件的独占锁。此方法启动一个操作以获取此通道文件的给定区域的锁。
handler参数是在获取锁(或操作失败)时调用的CompletionHandler对象。传递给CompletionHandler的结果是生成的FileLock。
调用此方法ch.lock(att, handler)的行为及方式与ch.lock(0L, Long.MAX_ VALUE, false,att, handler)
完全相同。参数A代表附件的数据类型。参数attachment代表要附加到I0操作的对象,可以为空。CompletionHandler 代表处理程序,用于消耗结果的处理程序。
public static void main(String[] args) throws Exception{
Path path = Paths.get("a.txt");
AsynchronousFileChannel channel = AsynchronousFileChannel.open(path, StandardOpenOption.WRITE);
System.out.println("begin time=" + System.currentTimeMillis());
channel.lock("我是附加值", new CompletionHandler<FileLock, String>() {
@Override
public void completed(FileLock result, String attachment) {
try {
System.out.println("public void completed(FileLock result, String attachment attachment=)" + attachment);
result.release();
channel.close();
System.out.println("release end close");
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void failed(Throwable exc, String attachment) {
System.out.println("public void failed(Throwable exc, String attachment) attachment=" + attachment);
System.out.println("getMessage=" + exc.getMessage());
}
});
System.out.println("end time=" + System.currentTimeMillis());
Thread.sleep(3000);
}
begin time=1591846382781
end time=1591846382784
public void completed(FileLock result, String attachment attachment=)我是附加值
release end close
可以发现,begin 和end的时间非常接近,几乎是相同的时间,这就是异步( asynchronized)的优势。
public void failed(Throwable exc, A attachment)方法调用时机
public void failed(Throwable exc, A attachment)
方法被调用的时机是出现I/O操作异常时。
public static void main(String[] args) throws Exception{
Path path = Paths.get("a.txt");
AsynchronousFileChannel channel = AsynchronousFileChannel.open(path,
StandardOpenOption.WRITE, StandardOpenOption.READ);
channel.close();
channel.lock("我是字符串我是附件", new CompletionHandler<FileLock, String>() {
@Override
public void completed(FileLock result, String attachment) {
try {
result.release();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void failed(Throwable exc, String attachment) {
System.out.println("public void failed(Throwable exc, String attachment) attachment");
System.out.println("attachment=" + attachment + " exc.getMessage()=" + exc.getMessage());
System.out.println("exc.getClass().getName()=" + exc.getClass().getName());
}
});
Thread.sleep(3000);
}
public void failed(Throwable exc, String attachment) attachment
attachment=我是字符串我是附件 exc.getMessage()=null
exc.getClass().getName()=java.nio.channels.ClosedChannelException
执行指定范围的锁定与传入附件及整合接口
public abstract <A> void lock(long position,long size,boolean shared,A attachment,CompletionHandler<FileLock,? super A> handler)
方法的作用是将public abstract Future<FileLock> lock(long position, long size, boolean shared)
方法和public final <A> void lock(A attachment,CompletionHandler<FileLock,? super A> handler)
方法进行了整合。
public static void main(String[] args) throws Exception{
Path path = Paths.get("a.txt");
AsynchronousFileChannel channel = AsynchronousFileChannel.open(path, StandardOpenOption.WRITE);
System.out.println("begin time=" + System.currentTimeMillis());
channel.lock(0, 3, false, "我是附加值", new CompletionHandler<FileLock, String>() {
@Override
public void completed(FileLock result, String attachment) {
try {
System.out.println("public void completed(FIleLock result, String attachment) attachment=" + attachment);
result.release();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void failed(Throwable exc, String attachment) {
System.out.println("public void failed(Throwable exc, String attachment) attachment=" + attachment);
System.out.println("getMessage=" + exc.getMessage());
}
});
System.out.println("end time=" + System.currentTimeMillis());
Thread.sleep(3000);
channel.close();
}
begin time=1591847712982
end time=1591847712985
public void completed(FIleLock result, String attachment) attachment=我是附加值
执行锁定与传入附件及整合接口CompletionHandler
如果public final <A> void lock(A attachment,CompletionHandler<FileLock,? super A> handler)
方法获得不到锁,则一直等待。
//A
public static void main(String[] args) throws Exception{
Path path = Paths.get("a.txt");
AsynchronousFileChannel channel = AsynchronousFileChannel.open(path, StandardOpenOption.WRITE);
System.out.println("A begin time=" + System.currentTimeMillis());
channel.lock("我是附加值A", new CompletionHandler<FileLock, String>() {
@Override
public void completed(FileLock result, String attachment) {
try {
Thread.sleep(9000);
result.release();
System.out.println("A release lock time=" + System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void failed(Throwable exc, String attachment) {
System.out.println("public void failed(Throwable exc, String attachment) attachment=" + attachment);
System.out.println("getMessage=" + exc.getMessage());
}
});
System.out.println("A end time=" + System.currentTimeMillis());
Thread.sleep(10000);
channel.close();
}
//B
public static void main(String[] args) throws Exception{
Path path = Paths.get("a.txt");
AsynchronousFileChannel channel = AsynchronousFileChannel.open(path, StandardOpenOption.WRITE);
System.out.println("B begin time=" + System.currentTimeMillis());
channel.lock("我是附加值B", new CompletionHandler<FileLock, String>() {
@Override
public void completed(FileLock result, String attachment) {
try {
System.out.println("public void completed(FileLock result, String attachment) attachment" + attachment);
result.release();
System.out.println("B get lock time=" + System.currentTimeMillis());
result.release();
channel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void failed(Throwable exc, String attachment) {
System.out.println("public void failed(Throwable exc, String attachment) attachment=" + attachment);
System.out.println("getMessage=" + exc.getMessage());
}
});
System.out.println("B end time=" + System.currentTimeMillis());
Thread.sleep(50000);
}
//A
A begin time=1591848196884
A end time=1591848196886
A release lock time=1591848205892
//B
B begin time=1591848198806
B end time=1591848198811
public void completed(FileLock result, String attachment) attachment我是附加值B
B get lock time=1591848205893
1591848205893减去1591848196884等于9009,说明A锁定了9s之后B才获得锁。