NIO多路复用模式中,用户线程在读写前可以不用阻塞等待数据到来,那么它去干什么了?

NIO多路复用模式中,用户线程在读写前可以不用阻塞等待数据到来,交给selector去监控,那么它去干什么了?

1、用户线程如果不阻塞,继续往下走,那我的应用程序岂不是没有数据返回,那不就是返回null了?

2、很多帖子说nio释放了资源,用户线程可以做其它事情,那这样岂不是线程不安全的?

 

在多路复用IO模型中,会有一个线程(Java中的Selector)不断去轮询多个socket的状态,只有当socket真正有读写事件时,才真正调用实际的IO读写操作。因为在多路复用IO模型中,只需要使用一个线程就可以管理多个socket,系统不需要建立新的进程或者线程,也不必维护这些线程和进程,并且只有在真正有socket读写事件进行时,才会使用IO资源,所以它大大减少了资源占用。

什么是线程安全,这个要弄清楚,当多个线程访问某个方法时,不管你通过怎样的调用方式或者说这些线程如何交替的执行,我们在主程序中不需要去做任何的同步,这个类的结果行为都是我们设想的正确行为,那么我们就可以说这个类是线程安全的。

单线程模式肯定是线程安全的

那我们看看多线程的实现代码

public class Reactor implements Runnable {
    public final Selector selector;
    public final ServerSocketChannel serverSocketChannel;


    public Reactor(int port) throws IOException {
        //用于监控fds
        selector=Selector.open();
        //socket服务器的chanel
        serverSocketChannel=ServerSocketChannel.open();

        InetSocketAddress inetSocketAddress=new InetSocketAddress(InetAddress.getLocalHost(),port);
        //
        serverSocketChannel.socket().bind(inetSocketAddress);
        //不设置阻塞队列
        serverSocketChannel.configureBlocking(false);

        //向selector注册该channel 返回selectionKey
        SelectionKey selectionKey=serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

        //利用selectionKey的attache功能绑定Acceptor 如果有事情,触发Acceptor
        selectionKey.attach(new Acceptor(this));
    }


    public void run() {
        try {
            while(!Thread.interrupted()){
                selector.select();//selector 阻塞
                Set<SelectionKey> selectionKeys= selector.selectedKeys();
                Iterator<SelectionKey> it=selectionKeys.iterator();
                //Selector如果发现channel有OP_ACCEPT或READ事件发生,下列遍历就会进行。
                while(it.hasNext()){
                    //来一个事件 第一次触发一个accepter线程
                    //以后触发SocketReadHandler
                    SelectionKey selectionKey=it.next();
                    dispatch(selectionKey);
                    selectionKeys.clear();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    /**
     * 运行Acceptor或SocketReadHandler
     * @param key
     */
    void dispatch(SelectionKey key) {
        Runnable r = (Runnable)(key.attachment());
        if (r != null){
            r.run();
        }
    }
}

运行结果不会受到这些线程如何交替的执行,而结果发生改变。

楼上的解答我理解,但是没有回答我的疑问,比如,我发送一个查询请求,那么通过NIO请求到服务端,这时候服务端需要用3秒处理。理论上在没有真正的读写时,客户端的用户线程是阻塞3秒等待服务端响应,还是交给selector监控,然后做别的事情了?