BIO,NIO,AIO详解

Posted truestoriesavici01

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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发生接入请求时,就会被唤醒.

示意图:
技术图片

参考:




以上是关于BIO,NIO,AIO详解的主要内容,如果未能解决你的问题,请参考以下文章

bio aio nio

bio aio nio

BIO NIO AIO 学习笔记

初理解Java中的BIO,NIO,AIO

Netty系列:基础篇 BIO-NIO-AIO

Java BIO与NIO以及AIO分析