Java IO 模型(1)

Java IO 模型

1 IO 模型

Java 支持 3 种 IO 模型:BIO、NIO、AIO

1.1 BIO(Blocking IO)

同步并阻塞,服务器实现模式为一个连接一个线程,即客户端 有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成 不必要的线程开销
在这里插入图片描述

1.2 NIO(NoneBlocking IO)

同步非阻塞,服务器实现模式为一个线程处理多个请求(连接),即客户端发 送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求就进行处理
在这里插入图片描述

1.3 AIO (NIO2.0)

异步非阻塞,AIO引入异步通道的概念,采用了Proactor模式,简 化了程序编写,有效的请求才启动线程,它的特点是先由操作系统完成后才通知服务端程

2 BIO(Blocking IO)

2.1 基本介绍

1.BIO 同步阻塞 一个连接一个线程
2.适用连接数较小、对服务器资源及并发数要求不太高的场景

2.2 原理图

在这里插入图片描述
1.server 端启动一个 ServerSocket
2.client 端启动 Socket 与 Server 端进行通信,默认情况下,client 端每启动一个 Socket 服务器端就会为其创建一个线程与之通信
3.客户端发送请求后,服务器先判断是否有线程响应,没有则等待或拒绝
4.如果有响应,客户端线程会等待请求结束后,再继续执行

2.3 实现

public class BIO {

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

        ServerSocket serverSocket = new ServerSocket(8099);

        while (true) {

            Socket socket = serverSocket.accept();

            System.out.println("连接到一个客户端... ");

            // 为连接到的客户端创建一个线程,处理该客户端的 IO 事件
            new Thread(() -> {
                handler(socket);
            }).start();

        }

    }

    public static void handler(Socket socket) {
        try {
            // 创建一个缓存
            byte[] buffer = new byte[1024];

            InputStream inputStream = socket.getInputStream();
            while (true) {
                // 一直轮询,等待处理 IO 事件

                int read = inputStream.read(buffer);

                if (read == -1) break;

                System.out.println(new String(buffer, 0, read, Charset.forName("utf-8")));
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}

telnet 127.0.0.1 8099
ctrl + ]
send xxx

在这里插入图片描述

2.4 问题分析

● 每个请求都需要创建独立的线程与对应的客户端进行 Read/Write
● 当并发数较大是,需要创建大量的线程来处理连接,系统资源占用大
● 建立连接后,如果当前线程暂时没有数据可读,则线程就会一直阻塞在 Read 方法上

3 NIO(NoneBlocking IO)

3.1 基本介绍

1.NIO 同步非阻塞
2.NIO 有三大核心部分:Channel(通道)、Buffer(缓冲区)、Selector(选择器)
3.NIO 是面向缓冲区,数剧的读写都是先放入缓冲区,并且数据可以在缓冲区中前后移动以此提高处理的灵活性
4.NIO 非阻塞模式,某个线程从某个通道(缓冲区)发送请求或读取数据时,它仅能够获取到可以读取的数据,如果缓冲区中没有数据,那么就什么都不会获取,而且线程并不会一直保持堵塞,而是去做其他事,写入同理
5.NIO 可以使用一个线程来处理多个请求

3.2 原理图

在这里插入图片描述
1.一个线程对应一个 selector,多个 channel
2.channel 会注册到 selector
3.selector 在 channel 中不断的切换,具体切换到那个 channel 由事件(event)决定
4.buffer 底层是一个数组
5.数据的读写都是通过 buffer,NIO 中的 buffer 即可以读也可以写,使用 flip 切换,因此 channel 也是双向的

3.3 NIO 三大核心组件 Buffer Channel Selector

在这里插入图片描述

3.4 缓冲区(Buffer)

public class NIOBuffer {

    public static void main(String[] args) {

        // 创建一块 int 类型 大小为 3 的缓冲区
        IntBuffer buffer = IntBuffer.allocate(5);

        // 向 buffer 中写入数据
        buffer.put(1);
        buffer.put(1);
        buffer.put(2);
        buffer.put(3);
        buffer.put(4);
//        buffer.put(5); // 超出缓冲器大小会报错 Exception in thread "main" java.nio.BufferOverflowException

        // buffer 读写切换
        buffer.flip();

        // 从buffer 中读取数据
        while (buffer.hasRemaining()) {
            System.out.println(buffer.get());
        }
        

    }

}

3.4.1 Buffer 子类

在这里插入图片描述
Buffer

//JDK1.4时,引入的api
public final int capacity( )  //返回此缓冲区的容量 
public final int position( )  //返回此缓冲区的位置
public final Buffer position (int newPositio)  //设置此缓冲区的位置 
public final int limit( )  //返回此缓冲区的限制
public final Buffer limit (int newLimit)  //设置此缓冲区的限制 
public final Buffer mark( )  //在此缓冲区的位置设置标记
public final Buffer reset( )  //将此缓冲区的位置重置为以前标记的位置
public final Buffer clear( )  //清除此缓冲区, 即将各个标记恢复到初始状态,但是数据并
public final Buffer flip( )  //反转此缓冲区
public final Buffer rewind( )  //重绕此缓冲区
public final int remaining( )  //返回当前位置与限制之间的元素数
public final boolean hasRemaining( )  //告知在当前位置和限制之间是否有元素
public abstract boolean isReadOnly( )  //告知此缓冲区是否为只读缓冲区

//JDK1.6时引入的api
public abstract boolean hasArray()  //告知此缓冲区是否具有可访问的底层实现数组 
public abstract Object array()  //返回此缓冲区的底层实现数组
public abstract int arrayOffset()  //返回此缓冲区的底层实现数组中第一个缓冲区元素的 
public abstract boolean isDirect()  //告知此缓冲区是否为直接缓冲区

IntBuffer

1.底层是一个 int 数组
2.一旦初始化大小便不可改变

在这里插入图片描述
在这里插入图片描述

3.4.2 Buffer 重要属性 mark position limit capacity

mark标记
position下一个要被读或写的元素的索引 每次读写缓冲区数据时都会改变改值, 为下次读写作准备
limit表示缓冲区的当前终点,不能对缓冲区 超过极限的位置进行读写操作。且极限 是可以修改的
capacity容量,即可以容纳的最大数据量;在缓 冲区创建时被设定并且不能改变

在这里插入图片描述

3.5通道(Channel)

3.5.1 基本介绍

在这里插入图片描述
1.NIO 的通道类似于流,但有如下区别

  • 通道可以同时进行读写,而流只能读或者只能写
  • 通道可以实现异步读写数据
  • 通道可以从缓冲读数据,也可以写数据到缓冲

2.BIO 中的流是单向的,只能对数据读或写,而 NIO 中的通道即可以读也可以写
3.常用的 channel
在这里插入图片描述

FileChannel 用于文件的数据读写
DatagramChannel 用于 UDP 的数据读写
ServerSocketChannel 和 SocketChannel 用于 TCP 的数据读写

3.5.2 FileChannel

FileChannel 主要用来对本地文件进行 IO 操作

public int read(ByteBufferdst),从通道读取数据并放到缓冲区中
public int write(ByteBuffer src),把缓冲区的数据写到通道中
public long transferFrom(ReadableByteChannel src,long position,long count),从目标通道 中复制数据到当前通道
public long transferTo(long position,long count,WritableByteChannel target),把数据从当前通道复制给目标通道

3.5.3 本地文件写数据

public class NIOChannelWrite {

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

        String data = "哈喽 Netty!";

        // 创建一个输出流
        FileOutputStream fileOutputStream = new FileOutputStream("files//1.txt");

        // 通过输出流获取对应的 channel , fileChannel 的实际类型是  FileChannelImpl
        FileChannel fileChannel = fileOutputStream.getChannel();

        // 创建一个 buffer
        ByteBuffer buffer = ByteBuffer.allocate(1024);

        // 将数据放入 buffer
        buffer.put(data.getBytes(StandardCharsets.UTF_8));

        // buffer 读写反转
        buffer.flip();

        // 将数据写入 channel
        fileChannel.write(buffer);

        // 关闭通道
        fileChannel.close();
        fileOutputStream.close();


    }

}

3.5.4 本地文件读数据

public class NIOChannelRead {

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

        File file = new File("files//1.txt");

        FileInputStream fileInputStream = new FileInputStream(file);

        FileChannel channel = fileInputStream.getChannel();

        ByteBuffer buffer = ByteBuffer.allocate((int) file.length());

        channel.read(buffer);

        System.out.println(new String(buffer.array()));

        channel.close();
        fileInputStream.close();

    }

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值