JavaNIO

javaNIO对于多路复用io(同步非阻塞io)的实现

package test;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;

public class NioPractice {

    public static void main(String[] args) throws IOException {
        // TODO Auto-generated method stub

        //开启一个ServerSocketChannel在端口8080监听
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.bind(new InetSocketAddress("localhost", 8080));
        //开启一个多路复用器,可以实现用一个线程监控所有IO连接的IO就绪事件
        
        Selector selector=Selector.open();
        //将severSocketChannel注册到多路复用器上,并声明关注其accept就绪事件
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        
        while(selector.select()!=0) {
            Iterator<SelectionKey> iterator=selector.selectedKeys().iterator();
            while(iterator.hasNext()) {
                SelectionKey key=iterator.next();
                if(key.isReadable()) {
                    //将Channel里的数据读出,当有大量数据需要读取时,导致监控线程一直为这个io服务,
                    //因此可以专门开一个线程池来对io读写处理(不由监控线程来处理,由新开的线程池来处理,所以监控线程可以去处理别的就绪事件)
                }
                if(key.isWritable()) {
                    
                }
                if(key.isAcceptable()) {
                    //统一由selector多路复用器来监听,当socketChannel为非阻塞时,省去了单独判断它是否成功接受连接
                    SocketChannel socketChannel=serverSocketChannel.accept();
                    //将socketChannel注册到多路复用器上,并声明关注其read事件
                    socketChannel.register(selector, SelectionKey.OP_READ);
                    
                    
                    
                }
                iterator.remove();
                
            }
        }
        
        
    }

}

基于 Java NIO 的事件分离模式

采用 Java NIO 技术实现的典型的两种高效、应用广泛的事件分离模式:一个是反应器(Reactor)模式,一个是前摄器(Proactor)模式。

 同步非阻塞io

反应器(Reactor)模式目的是使服务器具有并发处理服务请求的能力。服务器首先注册多个事件处理器(Event Handler),事件分离器(Event Demultiplexer)负责接收一个或者多个客户端发来的请求,并将事件分发到对应的事件处理器上。

一个典型的读操作的过程是:

1)  服务器程序注册 Accept 事件,事件分离器等待客户端的连接请求;

2)  客户端请求到来后,将连接事件分发到 Acceptor 事件处理器中,建立连接。

3)  连接建立完成后服务器程序再注册一个读操作事件。事件分离器继续等待事件发生;

4)  当读操作在对应的连接上就绪后,事 件分离器分发事件到对应的Read Handler 事件处理器中基于同步 I/O 进行阻塞读取;

5)  读取完成后再根据业务需要做进一步处理。 整个过程是由服务器感兴趣的事件触发的,因此形象的叫做反应器模式。


 异步非阻塞io

前摄器(Proactor)模式也是一种事件分离模式,与 Reactor 不同的是基于异步 I/O 的,实现了真正的无阻塞异步的 I/O 操作。前摄器模式同样是分离事件给对应的事件处理器,然而这里的事件不再是就绪事件,如就绪读或者就绪写,而是异步操作的完成事件。

一个典型的读操作处理过程如下:

1)  服务器程序注册 Accept 事件,事件分离器等待客户端的连接请求;

2)  客户端请求到来后,将连接事件分发到 Acceptor 事件处理器中,建立连接。

3)  连接建立完成后服务器程序再注册一个读操作完成事件。事件分离器继续读操作完成事件的发生;

4)  当读操作在对应的连接上就绪后,由操作系统触发一个异步读取操作,等读取完成后,会将读取内容放在用户指定的缓冲区,并通知应用程序事件完成。

5)  事件分离器接收到完成事件后,将事件分发到 ReadHandler 事件处理器中直接从指定缓冲区中取出数据,而不需要进行实际的 I/O 操作。

6)  缓冲区数据取出后再根据不同的业务做进一步的处理

 

典型的异步模式实现,都建立在操作系统支持异步 API 的基础之上,这种实现称为系统级异步,因为应用程序完全依赖操作系统执行真正的 I/O 工作 

 

这两个模式都使用了事件驱动模型:一个是在数据准备好时通知事件处理器去进行io处理,一个 是在已经完成了io处理后通知事件处理器进行业务处理

事件驱动模型图

使用JavaAIO对文件进行异步读写的实现如下:

package test;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousFileChannel;
import java.nio.channels.CompletionHandler;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;

public class AIOtest1 {

    public static void main(String[] args) throws IOException {
        // TODO Auto-generated method stub
        Path path = Paths.get("test-write.txt");
        if(!Files.exists(path)){
            Files.createFile(path);
        }
        AsynchronousFileChannel fileChannel = 
            AsynchronousFileChannel.open(path, StandardOpenOption.WRITE);

        ByteBuffer buffer = ByteBuffer.allocate(1024);
        long position = 0;

        buffer.put("test data".getBytes());
        buffer.flip();
        
        fileChannel.write(buffer, position, buffer, new CompletionHandler<Integer, ByteBuffer>() {

            @Override
            public void completed(Integer result, ByteBuffer attachment) {
                System.out.println("bytes written: " + result);
            }

            @Override
            public void failed(Throwable exc, ByteBuffer attachment) {
                System.out.println("Write failed");
                exc.printStackTrace();
            }
        });

    }

}

参考:

论文:基于Netty的高可服务消息中间件的研究与实现_崔晓旻

博客:Java进阶知识点5:服务端高并发的基石 - NIO与Reactor模式以及AIO与Proactor模式

Java NIO AsynchronousFileChannel

 

posted @ 2019-01-12 15:42  是甜甜啊  阅读(204)  评论(0编辑  收藏  举报