什么是AIO
AIO全称Asynchronous I/O即异步IO,要知道虽然NIO是非阻塞I/O,但是依然是同步进行的后续的任务需要在I/O读写完毕之后才能进行,而AIO无须等待IO的执行直接执行I/O之后的任务,而当IO处理完成后再通过回调的方式完成其他需要等待I/O完成后才能做的其他操作,所以AIO是jdk1.7对I/O做出的再优化
AIO是如果做到异步操作的呢?
首先AIO是需要操作系统的支持,根据之前的基础我们了解AIO使用的是Proactor模式,AIO将I/O的操作交给了操作系统,由操作系统将文件读取到缓冲区或从缓冲区写入到文件,AIO只用监听I/O读写完成的事件,而不用等待获取读就绪写就绪状态,程序就只用关心对应的业务操作。
AIO的特性
- 不用等待I/O操作完成,而是完成之后才进行通知
- 使用回调函数等待I/O操作完成后进行后续任务执行。
AIO的核心实现
- 在了解NIO后我们知道channel是自jdk1.4后对I/O传输的核心组成,在AIO后channel包下新增了几个类,以提供异步的IO操作。
- AIO核心类API解析
- AsynchronousFileChannel
-
StandardOpenOption(枚举)
- 该枚举类是用来定义文件异步通道的选项值,在就像NIO一样在创建通道的时候可以对通道设置其读写模式等。
- 枚举类属性说明:
- StandardOpenOption.READ 以读取方式对文件进行访问
- StandardOpenOption.WRITE: 以写入方式对文件进行访问
- StandardOpenOption.CREATE: 如果文件不存在则创建,如果与CREATE_NEW同时存在忽略当前选项
- StandardOpenOption.CREATE_NEW: 如果文件不存在则创建,如果存在则抛出异常
- StandardOpenOption.APPEND: 在文件的尾部追加数据
- StandardOpenOption.DELETE_ON_CLOSE: 在对通道进行关闭的时候对文件进行删除。
- StandardOpenOption.TRUNCATE_EXISTING: 当WRITE对文件进行写入时,该选项将当前文件截断为0,如果时READ方式则忽略该选项。
- StandardOpenOption.SPARSE: 在创建新的文件时,默认将其创建为稀疏文件,所谓稀疏文件是指该文件不会马上分配到实际大小的磁盘空间,当对文件写入的时候NTFS文件系统才会逐渐为文件分配剩余的磁盘空间。这样的好处是你能创建一个很大的文件,但是它不会马上占用那么多的空间,节省磁盘空间。
- StandardOpenOption.SYNC: 每次写入的内容和元数据都会进行同步磁盘存储
- StandardOpenOption.DSYNC: 每次对写入的内容进行同步的磁盘存储
-
API解析:
- force(boolean metaData); 该方法提供对文件内容的更新是否同步更新到磁盘的操作,设置为true则每次都同步。
- lock(); 锁定文件中的内容,从0开始到设置的Long.MAX_VALUE为止,并返回Future类的对象。
- lock(A attachment, CompletionHandler<FileLock,? super A> handler); 也是将文件内容锁定从0开始到Long.MAX_VALUE为止,但是该方法不在返回任何对象而是提供了锁定完成后调用自定义的回调方法。
- lock(long position, long size, boolean shared); 该方法也是对文件内容的锁定,但是其提供了对部分内容的定位,从position位置开始所动size长度的内容,之后的shared参数如果为true说明该锁为共享锁;并且该方法完成之后将会返回一个Future类的对象。
- lock(long position,ong size,boolean shared,A attachment,CompletionHandler<FileLock,? super A> handler); 该 方法同上,但是没有具体返回值并且锁定完成后调用自定义的回调方法。
- open(Path file, OpenOption… options); 创建一个异步通道,返回AsynchronousFileChannel对象,第一个传入Path类包装的文件路径,之后传入OpenOption类型的可变参,之前我们提到的StandardOpenOption就是该类的实现类,并且该参数是可变参,就可以根据需要对该通道指定多个方式。
- read(ByteBuffer dst, long position); 该方法将通道内的内容读取到缓冲区中,并且可以指定从position位置开始读取,返回Future 类对象,用于在未来查看任务是否执行完成。
- read(ByteBuffer dst,ong position,A attachment,CompletionHandler<Integer,? super A> handler); 方法作用同上,但是没有具体的返回值,而是完成操作后会调用自定义的回调方法。
- size(); 返回当前异步通道读取文件的大小。
- truncate(long size); 该方法会将此通道打开的文件截断到size大小。
- tryLock() 尝试获取锁,并返回一个FileLock对象,该对象可以是获取锁的新对象也可能为null因为可能是另一个程序获取了重叠锁。
- tryLock(long position, long size, boolean shared); 同tryLock()方法作用,只是该方法可对指定区域进行上锁,并且可以其是否为共享锁。
- write(ByteBuffer src, long position); 该方法用于将缓冲区的数据写入到通道,可以指定从position位置开始写入,方法返回Future 类对象,用于在未来查看任务是否执行完成。
- write(ByteBuffer src,ong position,A attachment, CompletionHandler<Integer,? super A> handler); 该方法作用同上,只是不在返回对象,而是在写入完成之后调用自定义的回调方法。
-
在了解了相关AIPI后我们发现所有的方法都提供了两种形式的调用,一个是通过Futturel类来访问任务是否完成,另外一种是通过创建CompletionHandler类自定义回调方法实现任务完成进行回调。应用实例:
- Future异步在未来获取
class FutureCopyFile{ public void copyFile(String sourcePath,String targetPath) { AsynchronousFileChannel channel1=null; AsynchronousFileChannel channel2=null; try { channel1=AsynchronousFileChannel.open(Paths.get(sourcePath),StandardOpenOption.READ); //open的第二个参数后续为StandardOpenOption的可变参数,可以设置该通道的为几种类型 channel2=AsynchronousFileChannel.open(Paths.get(targetPath),StandardOpenOption.WRITE, StandardOpenOption.READ,StandardOpenOption.CREATE); ByteBuffer buffer= ByteBuffer.allocate(1024); //创建一个position记录文件内上一次的读写位置,从零开始读写 long position=0; while (true){ buffer.clear(); //使用Future来接收结果 Future<Integer> read = channel1.read(buffer, position); //判断数据的长度 int len = read.get(); if(len<1) break; //读写转换 buffer.flip(); //将buffer中的数据写入文件 Future<Integer> write = channel2.write(buffer, position); //对读写位置做更改 position+=len; //直到写入完成才进入下一次读写 while (!write.isDone()); } }catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }finally { try { channel1.close(); channel2.close(); } catch (IOException e) { e.printStackTrace();} } } }
- CompletionHandler
class CallbackCopyFile { private AsynchronousFileChannel channel1=null; private AsynchronousFileChannel channel2=null; //创建一个position记录文件内上一次的读写位置,从零开始读写 private long position=0; private CompletionHandler<Integer,ByteBuffer> readHandler=null; private CompletionHandler<Integer,ByteBuffer> writeHandler=null; private volatile int num=0; public void copyFile(String sourcePath,String targetPath) { try { channel1=AsynchronousFileChannel.open(Paths.get(sourcePath),StandardOpenOption.READ); //open的第二个参数后续为StandardOpenOption的可变参数,可以设置该通道的为几种类型 channel2=AsynchronousFileChannel.open(Paths.get(targetPath),StandardOpenOption.WRITE,StandardOpenOption.READ,StandardOpenOption.CREATE); ByteBuffer buffer= ByteBuffer.allocate(1024); //创建一个CompletionHandler作为文件读取完成的回调处理 readHandler=new CompletionHandler<Integer,ByteBuffer>(){ //完成之后调用 @Override public void completed(Integer result, ByteBuffer attachment) { if(result<1){ num++; return; } attachment.flip(); channel2.write(attachment,position,attachment,writeHandler); } //失败的话调用该方法 @Override public void failed(Throwable exc, ByteBuffer attachment) { System.out.println("读取失败!"); } }; //创建一个CompletionHandler做为写入完成的回调处理 writeHandler=new CompletionHandler<Integer,ByteBuffer>(){ //完成之后调用 @Override public void completed(Integer result, ByteBuffer attachment) { position+=result; attachment.clear(); channel1.read(attachment,position,attachment,readHandler); } //失败的话调用该方法 @Override public void failed(Throwable exc, ByteBuffer attachment) { System.out.println("写入失败!"); } }; buffer.clear(); channel1.read(buffer,position,buffer,readHandler); while (true){ if(num==1)break; } } catch (IOException e) { e.printStackTrace(); }finally { try { channel1.close(); channel2.close(); } catch (IOException e) { e.printStackTrace();} } } }
- Future异步在未来获取
-
- AsynchronousSocketChannel、AsynchronousServerSocketChannel和AsynchronousChannelGroup
- StandardSocketOptions
- 该类提供了对异步通道的选项值设定,异步通道根据自身需要通过调用 setOption方法对异步通道参数进行定义。
- API解析:
- StandardSocketOptions.IP_MULTICAST_IF:互联网协议(IP)组播数据报的网络接口。此套接字选项的值为NetworkInterface ,表示由面向数据包的套接字发送的组播数据报的出站接口。默认为null,以表示操作系统将通常基于网络路由表选择输出接口。
- StandardSocketOptions.IP_MULTICAST_LOOP:互联网协议(IP)组播数据报的环回。此套接字选项的值为Boolean ,用于控制组播数据报的环回 。 套接字选项的值表示该选项是启用还是禁用。该选项值默认为true。
- StandardSocketOptions.IP_MULTICAST_TTL:时间到现场的互联网协议(IP)组播数据报,该选项值为Integer需要自定义其TTL(生存时间0~255),默认为1.
- StandardSocketOptions.IP_TOS:在IP头中,有一Type-of-Service字段,该字段描述了IP包的优先级和QoS选项,使用IP_TOS可以来设定该字段的值,以区分不同服务的优先级。此套接字选项值为Integer,默认为0。
- StandardSocketOptions.SO_BROADCAST:允许传输广播数据报,首先要了解在ip地址中存在一个ip地址为该ip段落内的广播地址,在像广播地址发送消息时,形同与像该ip段落的所有IP发送消息,该选项默认为ture。
- StandardSocketOptions.SO_KEEPALIVE:保持连接活着,通过发送心跳让连接保持,该选项值默认为false。
- StandardSocketOptions.SO_LINGER:如果数据存在,则关闭;首先该选项值的默认值为负数,表示不开启该选项。如果为正数则代表设置其延时关闭时间,在对通道进行关闭会等待延时时间(为负不等待),延时时间过去后如果缓冲区中存在数据,则抛弃缓冲区的数据。
- StandardSocketOptions.SO_RCVBUF:该选项值能改变该异步通道接收的缓冲区的大小,但是可能实际的大小可能不会相同该选项只是起一个暗示作用,此参数类型为Integer类型。
- StandardSocketOptions.SO_REUSEADDR:一般来说,一个端口释放后会等待两分钟之后才能再被使用,该是让端口释放后立即就可以被再次使用。
- StandardSocketOptions. SO_SNDBUF:该选项值能改变该异步通道发送的缓冲区的大小,但是可能实际的大小可能不会相同该选项只是起一个暗示作用,此参数类型为Integer类型。
- StandardSocketOptions. TCP_NODELAY:禁用Nagle算法意味着允许小包的发送。对于延时敏感型,同时数据传输量比较小的应用适用。此选项值默认为false。
- AsynchronousChannelGroup
- 该类的异步通道的一个分组管理器,同一分组管理器中的数据是共享的,该类对象的创建需要绑定一个线程池,如果没有创建该类对象,则程序会默认的创建一个线程池进行绑定作为程序默认异步通道组实现异步通道的管理,而该线程池中线程的作用:
- 一个是用来监听异步I/O的事件并发送到completion-handlers
- 另一个对可以执行支持执行异步I/O操作所需的其他任务。
- 该类的异步通道的一个分组管理器,同一分组管理器中的数据是共享的,该类对象的创建需要绑定一个线程池,如果没有创建该类对象,则程序会默认的创建一个线程池进行绑定作为程序默认异步通道组实现异步通道的管理,而该线程池中线程的作用:
- AsynchronousServerSocketChannel
- 该类提供了异步数据数据流监听soket,对客户端发出的soket数据进行监听接收
- API解析:
- accept(); 方法接收一个连接,并返回一个Future类对象,能够获取当前连接Socket的状态。
- accept(A attachment,CompletionHandler<AsynchronousSocketChannel,
? super A> handler); 该方法也是接收一个连接,但是是通过自定义CompletionHandler让其获取连接成功后调用CompletionHandler类的completed方法。 - bind(SocketAddress local); 将通道的套接字绑定到某个地址,并配置套接字以监听连接。
- bind(SocketAddress local, int backlog); 将通道的套接字绑定到某个地址,并配置套接字以监听连接,backlog参数是设置该通道的最大待定连接数,无参时是使用的系统默认的待定连接数。
- open(); 打开一个异步socket通道,该方法会使用程序默认的异步通道组
- open(AsynchronousChannelGroup group); 打开一个异步socket通道,但这里使用的是自定义的异步通道组对象。
- provider(); 该方法返回创建该通道的创建程序,异步通道提供程序是此AsynchronousChannelProvider类的具体子类
- setOption(SocketOption name, T value) 该方法用于设置异步通道socket的选项值。
- AsynchronousSocketChannel
- 面向流的连接套接字的异步通道。
- 常用API解析:
- bind(SocketAddress local); 将当前异步通道绑定到对应的ip和端口上。
- connect(SocketAddress remote); 连接到定义的IP和端口上,方法返回Future类对象,以此来获取连接是否成功。
- connect(SocketAddress remote, A attachment, CompletionHandler<Void,? super A> handler); 该方法也是连接到定义的IP和端口上,并通过定义的CompletionHandler类对象实现连接成功与否对方法的回调。
- open(); 打开一个异步socket通道,该方法会使用程序默认的异步通道组
- open(AsynchronousChannelGroup group); 打开一个异步socket通道,但这里使用的是自定义的异步通道组对象。
- provider(); 该方法返回创建该通道的创建程序,异步通道提供程序是此AsynchronousChannelProvider类的具体子类
- read(ByteBuffer dst); 读取通道中的数据到缓冲区中,并返回Future类对象以此来获取数据是否读取完毕。
- read(ByteBuffer dst, A attachment,CompletionHandler<Integer,? super A> handler); 读取通道中的数据到缓冲区中,传入自定义的CompletionHandler类对象,读取成功与否都实现方法的回调。
- setOption(SocketOption name, T value) 该方法用于设置异步通道socket的选项值。
- shutdownInput(); 该方法用于单向的关闭输入数据通道,但是不会关闭连接。
- shutdownOutput(); 该方法用于单向的关闭输出数据通道,但是不会关闭连接。
- write(ByteBuffer src); 将缓冲区的数据写入到通道中,并返回Future类对象以供判断是否写入完毕。
- write(ByteBuffer src, A attachment, CompletionHandler<Integer,? super A> handler) 将缓冲区的数据写入到通道中,写入成功与否会直接通过自定义的CompletionHandler类对象进行方法回调。
- 实例应用
- StandardSocketOptions
- AsynchronousFileChannel