前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >BIO和伪异步IO

BIO和伪异步IO

作者头像
黑洞代码
发布2021-01-14 15:37:05
4760
发布2021-01-14 15:37:05
举报

JDK1.0到JDK3.0中,Java IO类库中很多Unix网络编程中很多高级特性和接口都没有实现,如Pipe、Channel、Buffer和Selector等。

JDK1.4中,NIO以JSR-51的身份随JDK发布,其中增加了java.nio包。JDK1.7中对于原有的NIO类库进行了升级,被称为NIO2.0。

BIO通信

采用BIO模型的服务端,通常是用一个Acceptor线程监听客户端的连接,收到客户端请求后为每个客户端创建一个新的服务器端线程进行请求处理,处理完后将结果返回给客户端,销毁服务器端线程。

缺点:缺乏弹性伸缩能力。当客户端并发上升,服务器端线程会跟客户端线程成1:1的关系,导致服务端系统性能下降,最终导致系统宕机或奔溃。

BIO客户端

代码语言:javascript
复制
public class BioClient {
    public static void main(String[] args) {
        int port = 8888;
        Socket socket = null;
        BufferedReader in = null;
        PrintWriter out = null;
        try {
            socket = 
            new Socket("127.0.0.1", port);
            in = new BufferedReader(
                new InputStreamReader(
                    socket.getInputStream()));
            out = new PrintWriter(
                socket.getOutputStream()
                , true);
            out.println("hello world");
            String resp = in.readLine();
            System.out.println(resp);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                out.close();
                in.close();
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

BIO服务端

代码语言:javascript
复制
public class BioServer {

    public static void main(String[] args) {
        int port = 8888;
        ServerSocket serverSocket = null;
        try {
            serverSocket 
                = new ServerSocket(port);
            System.out.println("端口8888启动");
            Socket socket = null;
            while (true) {
             socket = serverSocket.accept();
             new Thread(
                new ServerHandler(socket))
                .start();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (serverSocket != null) {
                System.out.println("关闭服务端");
                try {
                    serverSocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

BIO处理线程

代码语言:javascript
复制
public class ServerHandler implements Runnable {
    private Socket socket;

    public ServerHandler (Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {
        BufferedReader in = null;
        PrintWriter out = null;
        try {
            in = new BufferedReader(
                new InputStreamReader(
                  this.socket
                    .getInputStream()));
            out = new PrintWriter(
                    this.socket
                    .getOutputStream(), true);
            String current = null;
            String body = null;
            while (true) {
                body = in.readLine();
                if (body == null) {
                    break;
                }
                System.out.println(
                    "获取客户端输入:" + body);
                out.println(
                  "当前时间是:" + new Date());
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                in.close();
                out.close();
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

伪异步IO

既然BIO会为每个客户端在服务器端生成一个处理线程,服务器端可以用来做优化。当有新的客户端接入时,将客户端socket封装成一个任务,提交到线程池去执行。由于线程池有一系列参数(核心线程,最大线程,阻塞队列)约束,因此线程池占用的资源是可控的,即无论多少客户端连接到服务器端,都不会导致服务器资源耗尽而宕机。

代码语言:javascript
复制
public class FakeNioServer {

    public static void main(String[] args) {
        int port = 8888;
        ServerSocket serverSocket = null;
        ExecutorService executorService = null;
        try {
            serverSocket = new ServerSocket(port);
            System.out.println("服务器端口8888启动");
            Socket socket = null;
            executorService = Executors
            .newFixedThreadPool(10);
            while (true) {
                socket 
                    = serverSocket.accept();
                executorService
                    .submit(
                    new ServerHandler(socket));
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (serverSocket != null) {
                System.out.println("关闭服务端");
                try {
                    serverSocket.close();
                    executorService.shutdown();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

虽然伪异步IO避免了为每个客户端创建线程,但是因其底层通信仍然是同步阻塞模型,因此没有从根本是解决这个问题。

看下BIO中的InputStream的源码:

代码语言:javascript
复制
/**
 * Reads the next byte of data from the input stream. The value byte is
 * returned as an <code>int</code> in the range <code>0</code> to
 * <code>255</code>. If no byte is available because the end of the stream
 * has been reached, the value <code>-1</code> is returned. This method
 * blocks until input data is available, the end of the stream is detected,
 * or an exception is thrown.
 *
 * <p> A subclass must provide an implementation of this method.
 *
 * @return     the next byte of data, or <code>-1</code> if the end of the
 *             stream is reached.
 * @exception  IOException  if an I/O error occurs.
 */
public abstract int read() throws IOException;

当对socket的输入流进行读操作时,他会一直阻塞住,直到发生以下三种事件:

1.有数据可读

2.可读数据已经读取完毕

3.抛出异常

如果发送数据的一方处理速度缓慢,需要1分钟才能把数据发送完成,数据读取方必须同步阻塞1分钟。

对于BIO中的输出流也是会阻塞。

当数据接收方处理缓慢的时候,由于其不能及时的从TCP缓冲区读取数据,导致数据发送方的TCP window size不断减小,直到为0 ,双方处于Keep-Alive状态,此时消息发送方将不能再向缓冲区写入数据,write操作被无限期阻塞,直到window size大于0或者发生IO异常。

如何解决BIO带来的这些缺点呢?

Java中的NIO将解决这些问题

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-09-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 落叶飞翔的蜗牛 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • BIO客户端
  • 伪异步IO
相关产品与服务
弹性伸缩
弹性伸缩(Auto Scaling,AS)为您提供高效管理计算资源的策略。您可设定时间周期性地执行管理策略或创建实时监控策略,来管理 CVM 实例数量,并完成对实例的环境部署,保证业务平稳顺利运行。在需求高峰时,弹性伸缩自动增加 CVM 实例数量,以保证性能不受影响;当需求较低时,则会减少 CVM 实例数量以降低成本。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档