2022.08.24 学习笔记

学习笔记

IO

BIO

同步阻塞式 IO

  • 阻塞IO非阻塞IO

这两个概念是程序级别的。主要描述的是程序请求操作系统IO操作后,如果IO资源没有准备好,那么程序该如何处理的问题: 前者等待;后者继续执行(并且使用线程一直轮询,直到有IO资源准备好了)

  • 同步IO非同步IO

这两个概念是操作系统级别的。主要描述的是操作系统在收到程序请求IO操作后,如果IO资源没有准备好,该如何响应程序的问题: 前者不响应,直到IO资源准备好以后;后者返回一个标记(好让程序和自己知道以后的数据往哪里通知),当IO资源准备好以后,再用事件机制返回给程序。

阻塞式同步IO的工作原理:

  • 服务器线程发起一个accept动作,询问操作系统 是否有新的socket套接字信息从端口X发送过来。

  • 注意,是询问操作系统。也就是说socket套接字的IO模式支持是基于操作系统的,那么自然同步IO/异步IO的支持就是需要操作系统级别的了。

  • 如果操作系统没有发现有套接字从指定的端口X来,那么操作系统就会等待。这样serverSocket.accept()方法就会一直等待。这就是为什么accept()方法为什么会阻塞: 它内部的实现是使用的操作系统级别的同步IO。

NIO

同步⾮阻塞 IO

Standard IO是对字节流的读写,在进行IO之前,首先创建一个流对象,流对象进行读写操作都是按字节 ,一个字节一个字节的来读或写。而NIO把IO抽象成块,类似磁盘的读写,每次IO操作的单位都是一个块,块被读入内存之后就是一个byte[],NIO一次可以读或写多个字节。

  • 通道

    通道 Channel 是对原 I/O 包中的流的模拟,可以通过它读取和写入数据。

    通道与流的不同之处在于,流只能在一个方向上移动(一个流必须是 InputStream 或者 OutputStream 的子类),而通道是双向的,可以用于读、写或者同时用于读写。

    通道包括以下类型:

    • FileChannel: 从文件中读写数据;
    • DatagramChannel: 通过 UDP 读写网络中数据;
    • SocketChannel: 通过 TCP 读写网络中数据;
    • ServerSocketChannel: 可以监听新进来的 TCP 连接,对每一个新进来的连接都会创建一个 SocketChannel
  • 发送给一个通道的所有数据都必须首先放到缓冲区中,同样地,从通道中读取的任何数据都要先读到缓冲区中。也就是说,不会直接对通道进行读写数据,而是要先经过缓冲区。缓冲区实质上是一个数组,但它不仅仅是一个数组。缓冲区提供了对数据的结构化访问,而且还可以跟踪系统的读/写进程。

    缓冲区包括以下类型:

    • ByteBuffer CharBuffer ShortBuffer IntBuffer LongBuffer FloatBuffer DoubleBuffer
        public static void fastCopy(String src, String dist) throws IOException{
            FileInputStream fin = new FileInputStream(src);
            FileChannel fcin = fin.getChannel();
            FileOutputStream fout = new FileOutputStream(dist);
            FileChannel fcout = fout.getChannel();
            //为缓冲区分配1024个字节
            ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
            while (true){
                int read = fcin.read(buffer);
               //-1 为遇到EOF 
                if(read == -1){
                    break;
                }
                //切换读写
                buffer.flip();
                fcout.write(buffer);
                //最后需要清空缓存区
                buffer.clear();
            }
        }
    
  • 选择器

    NIO 常常被叫做非阻塞 IO,主要是因为 NIO 在网络通信中的非阻塞特性被广泛使用。

    NIO 实现了 IO 多路复用中的 Reactor 模型,一个线程 Thread 使用一个选择器 Selector 通过轮询的方式去监听多个通道 Channel 上的事件,从而让一个线程就可以处理多个事件。

    通过配置监听的通道 Channel 为非阻塞,那么当 Channel 上的 IO 事件还未到达时,就不会进入阻塞状态一直等待,而是继续轮询其它 Channel,找到 IO 事件已经到达的 Channel 执行。

    因为创建和切换线程的开销很大,因此使用一个线程来处理多个事件而不是一个线程处理一个事件具有更好的性能。

在这里插入图片描述

套接字NIO实例

public class NIOServer {

    public static void main(String[] args) throws IOException {
        //创建选择器
        Selector selector = Selector.open();
        ServerSocketChannel ssChannel = ServerSocketChannel.open();
        ssChannel.configureBlocking(false);
        //将通道注册到选择器上
        //通道必须配置为非阻塞模式,否则使用选择器就没有任何意义了,
        //因为如果通道在某个事件上被阻塞,那么服务器就不能响应其它事件,
        //必须等待这个事件处理完毕才能去处理其它事件,显然这和选择器的作用背道而驰。
        ssChannel.register(selector, SelectionKey.OP_ACCEPT);
        //创建socket
        ServerSocket serverSocket = ssChannel.socket();
        InetSocketAddress address = new InetSocketAddress("127.0.0.1",8888);
        serverSocket.bind(address);
        //因为一次 select() 调用不能处理完所有的事件,并且服务器端有可能需要一直监听事件,
        //因此服务器端处理事件的代码一般会放在一个死循环内。
        while(true){
            //使用 select() 来监听到达的事件,它会一直阻塞直到有至少一个事件到达。
            selector.select();
            Set<SelectionKey> keys = selector.selectedKeys();
            Iterator<SelectionKey> keyIterator = keys.iterator();
            while (keyIterator.hasNext()){
                SelectionKey key = keyIterator.next();
                if(key.isAcceptable()){
                    ServerSocketChannel ssChannel1 = (ServerSocketChannel) key.channel();
                    SocketChannel sChannel = ssChannel1.accept();
                    sChannel.configureBlocking(false);
                    //这个新连接主要用于从客户端读取数据,这样下次循环就是可读的状态了
                    sChannel.register(selector, SelectionKey.OP_READ);
                } else if (key.isReadable()){
                    SocketChannel sChannel = (SocketChannel) key.channel();
                    System.out.println(readDataFromSocketChannel(sChannel));
                    sChannel.close();
                }
                keyIterator.remove();
            }
        }
    }

    private static String readDataFromSocketChannel(SocketChannel socketChannel) throws IOException {
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        StringBuilder data = new StringBuilder();
        while (true) {
            buffer.clear();
            int n = socketChannel.read(buffer);
            if(n == -1){
                break;
            }
            //切换缓冲区读写
            buffer.flip();
            final int limit = buffer.limit();
            char[] dst = new char[limit];
            for (int i = 0; i < limit; i++) {
                dst[i] = (char) buffer.get(i);
            }
            data.append(dst);
            buffer.clear();
        }
        return data.toString();
    }
}
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket("127.0.0.1", 8888);
        OutputStream out = socket.getOutputStream();
        String s = "hello world";
        out.write(s.getBytes());
        out.close();
    }
  • 内存映射文件

    内存映射文件 I/O 是一种读和写文件数据的方法,它可以比常规的基于流或者基于通道的 I/O 快得多。向内存映射文件写入可能是危险的,只是改变数组的单个元素这样的简单操作,就可能会直接修改磁盘上的文件。修改数据与将数据保存到磁盘是没有分开的。

    AIO

异步⾮堵塞 IO

异步IO则是采用“订阅-通知”模式: 即应用程序向操作系统注册IO监听,然后继续做自己的事情。当操作系统发生IO事件,并且准备好数据后,在主动通知应用程序,触发相应的函数。

在这里插入图片描述

Netty

Netty是一个高性能、异步事件驱动的NIO框架,提供了对TCP、UDP和文件传输的支持。作为当前最流行的NIO框架,Netty在互联网领域、大数据分布式计算领域、游戏行业、通信行业等获得了广泛的应用,一些业界著名的开源组件也基于Netty构建,比如RPC框架、zookeeper等。

参考链接:https://pdai.tech/md/java/io/java-io-bio.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值