十一 BIO NIO

2018-11-22  本文已影响0人  BeYearn
  1. 首先,需要澄清一些基本概念:
  1. BIO
    IO 都是同步阻塞模式,所以需要多线程以实现多任务处理
public class BioServer extends Thread {
    private ServerSocket serverSocket;
    private ExecutorService executor;
    
    public int getPort() {
        return serverSocket.getLocalPort();
    }

    public void run() {
        try {
            executor = Executors.newFixedThreadPool(8); //初始化一个线程池来执行
            serverSocket = new ServerSocket(0);
            while (true) {
                Socket socket = serverSocket.accept();
                RequestHandler requestHandler = new RequestHandler(socket);
                executor.execute(requestHandler);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (serverSocket != null) {
                try {
                    serverSocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

// 简化实现,不做读取,直接发送字符串
class RequestHandler extends Thread {
    private Socket socket;
    RequestHandler(Socket socket) {
        this.socket = socket;
    }
    @Override
    public void run() {
        try (PrintWriter out = new PrintWriter(socket.getOutputStream())) {
            out.println("Hello world!");
            out.flush();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

public static void main(String[] args) throws IOException {
        DemoServer server = new DemoServer();
        server.start();
        try (Socket client = new Socket(InetAddress.getLocalHost(), server.getPort())) {
            BufferedReader bufferedReader = new BufferedReader(new                   InputStreamReader(client.getInputStream()));
            bufferedReader.lines().forEach(s -> System.out.println(s));
        }
    }
 }
  1. NIO1
    利用了单线程轮询事件的机制,通过高效地定位就绪的 Channel,来决定做什么,仅仅 select 阶段是阻塞的,可以有效避免大量客户端连接时,频繁线程切换带来的问题,应用的扩展能力有了非常大的提高
public class NioServer extends Thread {
    public void run() {
        try (Selector selector = Selector.open();
             ServerSocketChannel serverSocket = ServerSocketChannel.open()) {// 创建 Selector 和 Channel
            serverSocket.bind(new InetSocketAddress(InetAddress.getLocalHost(), 8888));
            serverSocket.configureBlocking(false);
            // 注册到 Selector,并说明关注点
            serverSocket.register(selector, SelectionKey.OP_ACCEPT);
            while (true) {
                selector.select();// 阻塞等待就绪的 Channel,这是关键点之一 当有 Channel 发生接入请求,就会被唤醒。
                Set<SelectionKey> selectedKeys = selector.selectedKeys();
                Iterator<SelectionKey> iter = selectedKeys.iterator();
                while (iter.hasNext()) {
                    SelectionKey key = iter.next();
                    // 生产系统中一般会额外进行就绪状态检查
                    sayHelloWorld((ServerSocketChannel) key.channel());
                    iter.remove();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void sayHelloWorld(ServerSocketChannel server) throws IOException {
        try (SocketChannel client = server.accept();) {
            //通过 SocketChannel 和 Buffer 进行数据操作 发送一段字符串
            client.write(Charset.defaultCharset().encode("Hello world!"));
        }
    }
    // 省略了与前面类似的 main
}
  1. NIO2 (java7引入, 或叫AIO)
    之后介绍

  2. 由于nio实际上是同步非阻塞io,是一个线程在同步的进行事件处理,当一组事channel处理完毕以后,去检查有没有又可以处理的channel。这也就是同步+非阻塞。同步,指每个准备好的channel处理是依次进行的,非阻塞,是指线程不会傻傻的等待读。只有当channel准备好后,才会进行。
    那么就会有这样一个问题,当每个channel所进行的都是耗时操作时,由于是同步操作,就会积压很多channel任务,从而完成影响。那么就需要对nio进行类似负载均衡的操作,如用线程池去进行管理读写,将channel分给其他的线程去执行,这样既充分利用了每一个线程,又不至于都堆积在一个线程中,等待执行.

B和N通常是针对数据是否就绪的处理方式来区别
sync和async是对阻塞进行更深一层次的阐释,区别在于数据拷贝由用户线程完成还是内核完成,讨论范围一定是两个线程及以上了。

同步阻塞,从数据是否准备就绪到数据拷贝都是由用户线程完成 bio
同步非阻塞,数据是否准备就绪由内核判断,数据拷贝还是用户线程完成 nio
异步非阻塞,数据是否准备就绪到数据拷贝都是内核来完成 aio

所以真正的异步IO一定是非阻塞的。

多路复用IO即使有Reactor通知用户线程也是同步IO范畴,因为数据拷贝期间仍然是用户线程完成。
详细可参考 https://tech.meituan.com/nio.html?utm_source=tool.lu

上一篇下一篇

猜你喜欢

热点阅读