设为首页 加入收藏

TOP

Java 非阻塞 IO 和异步 IO(三)
2018-02-22 14:32:42 】 浏览:635
Tags:Java 阻塞 异步
给操作系统,也使得操作系统能够对我们的 IO 进行一定程度的优化。

在 Linux 中其实也是有异步 IO 系统实现的,但是限制比较多,性能也一般,所以 JDK 采用了自建线程池的方式。

本文还是以实用为主,想要了解更多信息请自行查找其他资料,下面对 Java 异步 IO 进行实践性的介绍。

总共有三个类需要我们关注,分别是 AsynchronousSocketChannel,AsynchronousServerSocketChannel 和 AsynchronousFileChannel,只不过是在之前介绍的 FileChannel、SocketChannel 和 ServerSocketChannel 的类名上加了个前缀 Asynchronous。

Java 异步 IO 提供了两种使用方式,分别是返回 Future 实例和使用回调函数。

1、返回 Future 实例

返回 java.util.concurrent.Future 实例的方式我们应该很熟悉,JDK 线程池就是这么使用的。Future 接口的几个方法语义在这里也是通用的,这里先做简单介绍。

  • future.isDone();

    判断操作是否已经完成,包括了正常完成、异常抛出、取消

  • future.cancel(true);

    取消操作,方式是中断。参数 true 说的是,即使这个任务正在执行,也会进行中断。

  • future.isCancelled();

    是否被取消,只有在任务正常结束之前被取消,这个方法才会返回 true

  • future.get();

    这是我们的老朋友,获取执行结果,阻塞。

  • future.get(10, TimeUnit.SECONDS);

    如果上面的 get() 方法的阻塞你不满意,那就设置个超时时间。

2、提供 CompletionHandler 回调函数

java.nio.channels.CompletionHandler 接口定义:

public interface CompletionHandler<V,A> {

    void completed(V result, A attachment);

    void failed(Throwable exc, A attachment);
}

注意,参数上有个 attachment,虽然不常用,我们可以在各个支持的方法中传递这个参数值

AsynchronousServerSocketChannel listener = AsynchronousServerSocketChannel.open().bind(null);

// accept 方法的第一个参数可以传递 attachment
listener.accept(attachment, new CompletionHandler<AsynchronousSocketChannel, Object>() {
    public void completed(
      AsynchronousSocketChannel client, Object attachment) {
          // 
      }
    public void failed(Throwable exc, Object attachment) {
          // 
      }
});

AsynchronousFileChannel

网上关于 Non-Blocking IO 的介绍文章很多,但是 Asynchronous IO 的文章相对就少得多了,所以我这边会多介绍一些相关内容。

首先,我们就来关注异步的文件 IO,前面我们说了,文件 IO 在所有的操作系统中都不支持非阻塞模式,但是我们可以对文件 IO 采用异步的方式来提高性能。

下面,我会介绍 AsynchronousFileChannel 里面的一些重要的接口,都很简单,读者要是觉得无趣,直接滑到下一个标题就可以了。

实例化:

AsynchronousFileChannel channel = AsynchronousFileChannel.open(Paths.get("/Users/hongjie/test.txt"));

一旦实例化完成,我们就可以着手准备将数据读入到 Buffer 中:

ByteBuffer buffer = ByteBuffer.allocate(1024);
Future<Integer> result = channel.read(buffer, 0);

异步文件通道的读操作和写操作都需要提供一个文件的开始位置,文件开始位置为 0

除了使用返回 Future 实例的方式,也可以采用回调函数进行操作,接口如下:

public abstract <A> void read(ByteBuffer dst,
                              long position,
                              A attachment,
                              CompletionHandler<Integer,? super A> handler);

顺便也贴一下写操作的两个版本的接口:

public abstract Future<Integer> write(ByteBuffer src, long position);

public abstract <A> void write(ByteBuffer src,
                               long position,
                               A attachment,
                               CompletionHandler<Integer,? super A> handler);

我们可以看到,AIO 的读写主要也还是与 Buffer 打交道,这个与 NIO 是一脉相承的。

另外,还提供了用于将内存中的数据刷入到磁盘的方法:

public abstract void force(boolean metaData) throws IOException;

因为我们对文件的写操作,操作系统并不会直接针对文件操作,系统会缓存,然后周期性地刷入到磁盘。如果希望将数据及时写入到磁盘中,以免断电引发部分数据丢失,可以调用此方法。参数如果设置为 true,意味着同时也将文件属性信息更新到磁盘。

还有,还提供了对文件的锁定功能,我们可以锁定文件的部分数据,这样可以进行排他性的操作。

public abstract Future<FileLock> lock(long position, long size, boolean shared);

position 是要锁定内容的开始位置,size 指示了要锁定的区域大小,shared 指示需要的是共享锁还是排他锁

当然,也可以使用回调函数的版本:

public abstract <A> void lock(long position,
                              long size,
                              boolean shared,
                              A attachment,
                              CompletionHandler<FileLock,? super A> handler);

文件锁定功能上还提供了 tryLock 方法,此方法会快速返回结果:

public abstract FileLock tryLock(long position, long size, boolean shared)
    throws IOException;

这个方法很简单,就是尝试去获取锁,如果该区域已被其他线程或其他应用锁住,那么立刻返回 null,否则返回 Fi

首页 上一页 1 2 3 4 5 6 下一页 尾页 3/6/6
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇通向架构师的道路(第十二天)之A.. 下一篇正确的打日志姿势

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目