欢迎访问昆山宝鼎软件有限公司网站! 设为首页 | 网站地图 | XML | RSS订阅 | 宝鼎邮箱 | 宝鼎售后问题提交 | 后台管理


新闻资讯

MENU

软件开发知识
原文出处: JavaDoop

上一篇文章先容了 Java NIO 中 Buffer、Channel 和 Selector 的根基操纵,主要是一些接口操纵,较量简朴。

本文将先容非阻塞 IO 和异步 IO,也就是各人耳熟能详的 NIO 和 AIO。许多初学者大概分不清楚异步和非阻塞的区别,只是在各类场所能听到异步非阻塞这个词。

本文会先先容并演示阻塞模式,然后引入非阻塞模式来对阻塞模式举办优化,最后再先容 JDK7 引入的异步 IO,由于网上关于异步 IO 的先容相对较少,所以这部门内容我会先容得详细一些。

但愿看完本文,读者可以对非阻塞 IO 和异步 IO 的迷雾看得更清晰些,可能为初学者解开一丝丝迷惑也是好的。

NIO,JDK1.4,New IO,Non-Blocking IO

NIO.2,JDK7,More New IO,Asynchronous IO,严格地说 NIO.2 不只仅引入了 AIO

阻塞模式 IO

我们已经先容过利用 Java NIO 包构成一个简朴的客户端-处事端网络通讯所需要的 ServerSocketChannel、SocketChannel 和 Buffer,我们这里整合一下它们,给出一个完整的可运行的例子:

public class Server {

    public static void main(String[] args) throws IOException {

        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

        // 监听 8080 端口进来的 TCP 链接
        serverSocketChannel.socket().bind(new InetSocketAddress(8080));

        while (true) {

            // 这里会阻塞,直到有一个请求的毗连进来
            SocketChannel socketChannel = serverSocketChannel.accept();

            // 开启一个新的线程来处理惩罚这个请求,然后在 while 轮回中继承监听 8080 端口
            SocketHandler handler = new SocketHandler(socketChannel);
            new Thread(handler).start();
        }
    }
}

这里看一下新的线程需要做什么,SocketHandler:

public class SocketHandler implements Runnable {

    private SocketChannel socketChannel;

    public SocketHandler(SocketChannel socketChannel) {
        this.socketChannel = socketChannel;
    }

    @Override
    public void run() {

        ByteBuffer buffer = ByteBuffer.allocate(1024);
        try {
            // 将请求数据读入 Buffer 中
            int num;
            while ((num = socketChannel.read(buffer)) > 0) {
                // 读取 Buffer 内容之前先 flip 一下
                buffer.flip();

                // 提取 Buffer 中的数据
                byte[] bytes = new byte[num];
                buffer.get(bytes);

                String re = new String(bytes, "UTF-8");
                System.out.println("收到请求:" + re);

                // 回应客户端
                ByteBuffer writeBuffer = ByteBuffer.wrap(("我已经收到你的请求,你的请求内容是:" + re).getBytes());
                socketChannel.write(writeBuffer);

                buffer.flip();
            }
        } catch (IOException e) {
            IOUtils.closeQuietly(socketChannel);
        }
    }
}

最后,贴一下客户端 SocketChannel 的利用,客户端较量简朴:

public class SocketChannelTest {
    public static void main(String[] args) throws IOException {
        SocketChannel socketChannel = SocketChannel.open();
        socketChannel.connect(new InetSocketAddress("localhost", 8080));

        // 发送请求
        ByteBuffer buffer = ByteBuffer.wrap("1234567890".getBytes());
        socketChannel.write(buffer);

        // 读取响应
        ByteBuffer readBuffer = ByteBuffer.allocate(1024);
        int num;
        if ((num = socketChannel.read(readBuffer)) > 0) {
            readBuffer.flip();

            byte[] re = new byte[num];
            readBuffer.get(re);

            String result = new String(re, "UTF-8");
            System.out.println("返回值: " + result);
        }
    }
}

上面先容的阻塞模式的代码应该很好领略:来一个新的毗连,我们就新开一个线程来处理惩罚这个毗连,之后的操纵全部由谁人线程来完成。

那么,这个模式下的机能瓶颈在那边呢?

  1. 首先,每次来一个毗连都开一个新的线程这必定是不符合的。当活泼毗连数在几十几百的时候虽然是可以这样做的,但假如活泼毗连数是几万几十万的时候,这么多线程明明就不可了。每个线程都需要一部门内存,内存会被迅速耗损,同时,线程切换的开销很是大。
  2. 其次,阻塞操纵在这里也是一个问题。首先,accept() 是一个阻塞操纵,当 accept() 返回的时候,代表有一个毗连可以利用了,我们这里是顿时就新建线程来处理惩罚这个 SocketChannel 了,可是,可是这里不代表对方就将数据传输过来了。所以,SocketChannel#read 要领将阻塞,期待数据,明明这个期待是不值得的。同理,write 要领也需要期待通道可写才气执行写入操纵,这边的阻塞期待也是不值得的。

非阻塞 IO

说完了阻塞模式的利用及其缺点今后,我们这里就可以先容非阻塞 IO 了。