zoukankan      html  css  js  c++  java
  • java BIO/NIO

     

    一、BIO

    Blocking IO(即阻塞IO);

    1.      特点:

    a)   Socket服务端在监听过程中每次accept到一个客户端的Socket连接,就要处理这个请求,而此时其他连接过来的客户端只能阻塞等待;

    b)   多线程处理多个连接,每个线程拥有自己的栈空间并且占用一些 CPU 时间。每个线程遇到外部未准备好的时候,都会阻塞掉。阻塞的结果就是会带来大量的线程上下文切换。且大部分线程上下文切换可能是无意义的。比如假设一个线程监听一个端口,一天只会有几次请求进来,但是该 cpu 不得不为该线程不断做上下文切换尝试,大部分的切换以阻塞告终。

    2.      流程:

     

    二、 NIO

    Java New IO(即非阻塞IO )

    1.      特点:

    a)   由一个专门的线程来处理所有的 IO 事件,并负责分发。

    b)   事件驱动机制:事件到的时候触发,而不是同步的去监视事件。

    c)   线程通讯:线程之间通过 wait,notify 等方式通讯。保证每次上下文切换都是有意义的。减少无谓的进程切换。

    2.      示例:

    服务器端:

    ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

    serverSocketChannel.socket().bind(new InetSocketAddress(9999));

    while(true){

        SocketChannel socketChannel = serverSocketChannel.accept();

        //do something with socketChannel...

    }

    客户端:

    SocketChannel socketChannel = SocketChannel.open();

    socketChannel.connect(new InetSocketAddress("http://jenkov.com", 80));

    ByteBuffer buf = ByteBuffer.allocate(48);

    int bytesRead = socketChannel.read(buf);

    String newData = "New String to write to file";

    ByteBuffer buf = ByteBuffer.allocate(48);

    buf.clear();

    buf.put(newData.getBytes());

    buf.flip();

    while(buf.hasRemaining()) {

        channel.write(buf);

    }

    3.      Channels(通道):

     

    重要实现类:

    1)   FileChannel:从文件中读写数据。

    2)   DatagramChannel:能通过UDP读写网络中的数据。

    3)   SocketChannel:能通过TCP读写网络中的数据。

    4)   ServerSocketChannel:可以监听新进来的TCP连接,像Web服务器那样。对每一个新进来的连接都会创建一个SocketChannel。

     

    重要方法:

    Read(buf):从通道中读出数据到buf中;

    Write(buf):将buf中的数据写入到通道中;

    其中FileChanel提供了Map方法,返回一个MappedByteBuffer对象,代表内存映射文件IO,它可以比常规的基于流或者基于通道的I/O快的多。内存映射文件I/O是通过使文件中的数据出现为 内存数组的内容来完成的。

    4.      Buffers(缓冲区):

    本质上是一块可以写入数据,然后可以从中读取数据的内存。用于和NIO通道进行交互(数据是从通道读入缓冲区,从缓冲区写入到通道中的);

    继承关系:

     

    重要属性:

    序号

    名称

    属性功能

    说明

    1

    capacity

    缓冲区大小

    缓冲内存区域总的字节大小;

    2

    limit

    实际数据大小

    读模式时, limit表示你最多能读到多少数据;写模式下,表示你最多能往Buffer里写多少数据;写模式下,limit等于Buffer的capacity。

    3

    position

    当前位置

    表示当前的位置。初始的position值为0,position最大可为capacity–1;

     

     

     

     

     

    重要方法:

    序号

    方法名

    方法功能

    说明

    1

    Flip

    切换到读模式

    用于从写模式切换到读模式时调用的一个方法,表示最多能读取到多少内容;

    具体实现为: 将position设回0,并将limit设置成之前position的值;

    2

    rewind

    重读Buffer中的所有数据;

    将position设回0,limit保持不变,仍然表示能从Buffer中读取多少数据;

    3

    Clear

    compact

    清空缓冲区

    limit用于将整个缓冲区全部清空,而compact用于在缓冲区还有部分未读完的数据时,将未读完的数据拷贝到缓冲区最前面,将其余部分全部清空;

    limit和compact在清除缓冲区时都是修改的指针,使得往缓冲区里面写入数据时能覆盖,并不会真正清空缓冲区里面的内容;

    4

    Mark

    reset

    标记

    恢复标记

    mark()方法标记Buffer中的一个特定position。通过调用reset()方法恢复这个position;

    5

    Equals

    compareTo

    判断是否相等

    比较大小

    equals相等的条件:

    1.有相同的类型(byte、char、int等)。

    2.Buffer中剩余的byte、char等的个数相等。

    3.Buffer中所有剩余的byte、char等都相同。

    compareTo:判断Buffer A小于Buffer B的条件:

    1.第一个不相等的元素小于另一个Buffer中对应的元素 。

    2.所有元素都相等,但第一个Buffer比另一个先耗尽(第一个Buffer的元素个数比另一个少)。

    备注:剩余元素是从 positionlimit之间的元素

    6

    slice

    缓冲区分片

    根据现有的缓冲区对象来创建一个子缓冲区,即在现有缓冲区上切出一片来作为一个新的缓冲区,但现有的缓冲区与创建的子缓冲区在底层数组层面上是数据共享的,也就是说,子缓冲区相当于是现有缓冲区的一个视图窗口

    7

    asReadOnlyBuffer

    创建只读缓冲区

    这个方法返回一个与原缓冲区完全相同的缓冲区,并与原缓冲区共享数据,只不过它是只读的。如果原缓冲区的内容发生了变化,只读缓冲区的内容也随之发生变化;

    8

    allocateDirect

    直接缓冲区

    给定一个直接字节缓冲区,Java虚拟机将尽最大努 力直接对它执行本机I/O操作。也就是说,它会在每一次调用底层操作系统的本机I/O操作之前(或之后),尝试避免将缓冲区的内容拷贝到一个中间缓冲区中 或者从一个中间缓冲区中拷贝数据。

    5.      Selectors(选择器):

    选择器用于监听多个通道的事件(比如:连接打开,数据到达)。因此,单个的线程可以监听多个数据通道。

    要使用Selector,得向Selector注册Channel,然后调用它的select()方法,接下来就等待事件就绪并处理事件;

     

    使用事项:

    1)   与Selector一起使用时,Channel必须处于非阻塞模式下。这意味着不能将FileChannel与Selector一起使用,因为FileChannel不能切换到非阻塞模式。而套接字通道都可以。

    2)   选择器监听的事件集合有:SelectionKey.OP_CONNECT, SelectionKey.OP_ACCEPT, SelectionKey.OP_READ, SelectionKey.OP_WRITE;

    3)   从SelectionKey中分离出事件的方法:

    int interestSet = selectionKey.interestOps();

    boolean isInterestedInAccept=(interestSet & SelectionKey.OP_ACCEPT) == SelectionKey.OP_ACCEPT;

    boolean isInterestedInConnect = (interestSet & SelectionKey.OP_CONNECT) == SelectionKey.OP_CONNECT;

    boolean isInterestedInRead = (interestSet & SelectionKey.OP_READ) == SelectionKey.OP_READ;

    boolean isInterestedInWrite = (interestSet & SelectionKey.OP_WRITE) == SelectionKey.OP_WRITE;

    同样SelectionKey中也封装了上面的方法:

    selectionKey.isAcceptable();

    selectionKey.isConnectable();

    selectionKey.isReadable();

    selectionKey.isWritable();

    使用流程:

     

  • 相关阅读:
    ActiveMQ 默认用户名和密码
    # ActiveMQ连接超时问题(java.net.SocketException: Connection reset)
    SpringBoot(十) Logback 配置详解
    postgresql10以上的自动分区分表功能
    基于Redis实现延时队列服务
    Redis(十三):Redis分布式锁的正确实现方式
    Redis(十七):批量操作Pipeline
    Redis(十八):Redis和队列
    PostgreSQL SELECT INTO和INSERT INTO SELECT 两种表复制语句
    PostgreSQL 从文件时间戳获悉一些信息(如数据库创建时间)
  • 原文地址:https://www.cnblogs.com/laoxia/p/7672614.html
Copyright © 2011-2022 走看看