• BIO,NIO,AIO详解


    Java中的IO,BIO,NIO,AIO详解

    IO

    BIO,NIO和AIO的关系

    1. BIO:java.io包.基于流模型实现,使用同步,阻塞方式.即:读输入流或写输出流时,在读或写动作完成之前,读(写)线程一直阻塞.性能差.
    2. NIO:java.nio包.可以构建多路复用,同步非阻塞的IO操作.
    3. AIO:Java 1.7之后引入的包.提供异步非阻塞的IO操作.基于事件和回调机制实现的.程序操作后直接返回,不阻塞,后台完成处理后,由系统通知相应的线程进行后续的操作.

    IO的分类

    • InputStream,OutputStream:基于字节操作的IO.
    • Writer,Reader:基于字符操作的IO.
    • File:基于磁盘操作的IO.
    • Socket:基于网络操作的IO.

    IO的使用

    InputStream的使用

    InputStream inputStream = new FileInputStream("src\IOBIONIOAIO.md");
    byte[] bytes = new byte[inputStream.available()];
    inputStream.read(bytes);
    String str = new String(bytes);
    inputStream.close();
    System.out.println(str);
    

    OutputStream的使用

    OutputStream outputStream = null;
    outputStream = new FileOutputStream("src\IOBIONIOAIO.md",true);
    outputStream.write("
    OutputStream测试".getBytes("utf-8"));
    outputStream.close();
    

    Writer的使用

    Writer writer = new FileWriter("src\IOBIONIOAIO.md",true);
    writer.append("
    使用Writer向文件写入数据");
    writer.close();
    

    Reader的使用

    Reader reader = new FileReader("src\IOBIONIOAIO.md");
    BufferedReader br = new BufferedReader(reader);
    StringBuffer sb = new StringBuffer();
    String str = null;
    while ((str = br.readLine()) != null){
        sb.append(str+"
    ");
    }
    br.close();
    reader.close();
    System.out.println(sb.toString());
    

    同步,异步,阻塞,非阻塞

    同步与异步

    同步

    • 一个任务的完成依赖于另外一个任务.
    • 只有被依赖的任务完成后,依赖的任务才算完成.

    异步

    • 依赖的任务通知被依赖的任务要完成的任务后,完成自身的任务即算作完成.
    • 被依赖的任务是否真正完成,依赖的任务无法确定.

    阻塞和非阻塞

    阻塞

    调用结果返回之前,当前线程会被挂起.调用线程只有在得到结果之后才返回.

    非阻塞

    在没有得到结果之前,不会阻塞当前线程.
    线程可以在结果返回之前完成其他任务,提高了CPU的利用率.

    问题:系统的线程切换增加,需要权衡增加的CPU使用时间和线程切换的成本.

    同/异,阻/非阻塞的组合

    组合方式 性能
    同步阻塞 I/O性能差,CPU大部分在空闲状态
    同步非阻塞 提升I/O性能,增加CPU消耗
    异步阻塞 对I/O能够提升效率
    异步非阻塞 集群之间的消息同步机制

    文件读写

    // 使用FileWriter向文件写入数据
    public void fileWriterTest() throws IOException {
        FileWriter fw = new FileWriter("src\IOBIONIOAIO.md",true);
        fw.write("
    来自FileWriter写入的内容");
        fw.close();
    }
    
    // 使用FileReader读取文件的内容
    public void fileReaderTest() throws IOException {
        FileReader fr = new FileReader("src\IOBIONIOAIO.md");
        BufferedReader br = new BufferedReader(fr);
        StringBuffer sb = new StringBuffer();
        String str;
        while ((str = br.readLine())!=null){
            sb.append(str+"
    ");
        }
        br.close();
        fr.close();
        System.out.println(sb.toString());
    }
    

    使用java.nio包中的Files实现对文件的读写:

    public void filesWrite() throws IOException {
        Files.write(Paths.get("src\IOBIONIOAIO.md"),"
    使用Files向文件写入数据".getBytes(), StandardOpenOption.APPEND);
    }
    
    public void filesRead() throws IOException {
        byte[] bytes = Files.readAllBytes(Paths.get("src\IOBIONIOAIO.md"));
        System.out.println(new String(bytes).toString());
    }
    

    Socket和NIO的多路复用

    传统的Socket实现

    服务器只给客户端发消息.
    客户端将接收到的消息打印.

    /**
    * 服务器端:
    * 调用accept()阻塞线程等待客户端连接.
    * 客户端连接后将数据传输过去
    */
    public void servers(){
        Thread thread = new Thread(() -> {
            try {
                ServerSocket serverSocket = new ServerSocket(port);
                while (true) {
                    Socket socket = serverSocket.accept();
                    try (PrintWriter printWriter = new PrintWriter(socket.getOutputStream())) {
                        printWriter.println("来自于服务器端的消息");
                        printWriter.flush();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        });
        thread.start();
    }
    
    /**
    * 客户端:
    * 打开指定的socket端口,
    * 使用BufferedReader读取端口传来的数据
    */
    public void client(){
        try (Socket socket = new Socket(InetAddress.getLocalHost(),port)){
            BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            br.lines().forEach(s-> System.out.println(s));
        } catch (UnknownHostException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    

    示意图:

    NIO多路复用

    public void servers(){
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(4, 4, 60l, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
        threadPool.execute(()->{
            try (Selector selector =  Selector.open();
                    ServerSocketChannel serverSocketChannel =  ServerSocketChannel.open()){
                serverSocketChannel.bind(new InetSocketAddress(InetAddress.getLocalHost(),port));
                serverSocketChannel.configureBlocking(false);
                serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
                while (true){
                    selector.select();
                    Set<SelectionKey> selectionKeys = selector.selectedKeys();
                    Iterator<SelectionKey> iterator = selectionKeys.iterator();
                    while (iterator.hasNext()){
                        SelectionKey key = iterator.next();
                        try(SocketChannel channel = ((ServerSocketChannel)key.channel()).accept()){
                            channel.write(Charset.defaultCharset().encode("来自服务器通过NIO写入数据"));
                        }
                        iterator.remove();
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        });
    }
    
    public void client(){
        try (Socket cSocket =  new Socket(InetAddress.getLocalHost(),port)){
            BufferedReader br = new BufferedReader(new InputStreamReader(cSocket.getInputStream()));
            br.lines().forEach(s-> System.out.println(s));
        } catch (UnknownHostException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    

    流程:

    • 通过Selector.open()建立一个Selector,充当调度员.
    • 创建一个ServerSocketChannel,并向Selector注册,通过指定的SelectionKey.OP_ACCEPT告诉调度员,关注的是新的连接请求.
    • 配置非阻塞模式,否则注册行为抛出异常.
    • Selector阻塞在select操作,当有Channel发生接入请求时,就会被唤醒.

    示意图:

    参考:

  • 相关阅读:
    【CYH-02】noip2018数论模拟赛:赛后题解
    C语言 malloc函数
    C 库函数
    C语言strcmp()函数:比较两个字符串
    C语言sprintf函数的深入理解
    C语言strcat()函数:字符串连接(拼接)
    liunx 中设置zookeeper 自启动(service zookeeper does not support chkconfig)
    页面上出现403错误,并解决
    Mac 下安装nginx
    nginx: [emerg] unknown directive "," in /usr/local/etc/nginx/nginx.conf:80
  • 原文地址:https://www.cnblogs.com/truestoriesavici01/p/13217272.html
Copyright © 2020-2023  润新知