JAVA NIO模型

NIO结构

BIO和NIO的区别

NIO三大核心部分

  • Channel
  • Buffer
  • Selector

1. NIO结构(java.nio包)


2. BIO和NIO区别

  • BIO面向流,基于字节流和字符流
  • NIO面向块(Buffer),基于Channel(通道)和Buffer(缓冲区) 
  • BIO是阻塞的,NIO是非阻塞的

3. 三大核心

3.1 Buffer

(1)继承关系

  • 主要有8种
    • 基本类型7种(都为abstract类):除了booleanBuffer没有,剩下的基本类型都有对应的Buffer类
      • 具体实现类为 HeapXXXBuffer
    • ​​​​​​MappedByteBuffer(extends ByteBuffer)是专门用于内存映射的一种ByteBuffer

​​​​​

(2)内部结构

属性 
private int mark = -1;用于记录当前position的前一个位置或者默认是-1
private int position = 0;下一个要操作的数据元素的位置
private int limit;

缓冲区数组中不可操作的下一个元素的位置:limit<=capacity

  • 读模式时:limit的值会被设置成写模式的position值
  • 写模式时:limit的值会被设置成capacity
private int capacity;
  • capacity一旦初始化,就不能不会改变
    • Buffer对象在初始化时,会按照capacity分配内部的内存。内存分配好后,大小就不能变了。分配内存时,一般使用Buffer的抽象子类ByteBuffer.allocate()方法,实际上是生成ByteArrayBuffer类
  • 缓冲区数组的大小值,指的不是内存的字节的数量,而是写入的对象的数量
    • 比如定义了一个capacity=100的DoubleBuffer,那么我们最多可以写入100个double 数据
final int[] hb;以intBuffer为例,实际的存储数组
  • 总结
    • capacity >= limit >= position >= mark >= 0
  • 例子
    • 初始调用ByteBuffer.allocate(n)时:position=0,limit = capacity = n
    • 如果这时写入5字节:position=5, limit = capacity = n
    • 调用ByteBuffer.flip()方法,将缓冲区的5字节写入Channel通道:position = 0, limit = 5, capacity = n

(3)方法,以IntBuffer为例

Buffer抽象类方法

获取相关属性

capacity

position

limit

public final int capacity()返回缓冲区的capacity

public final int position()

public final Buffer position(int newPosition)

返回/设置 缓冲区的position

public final int limit()

public final Buffer limit(int newLimit)

返回/设置 缓冲区的limit
重置 clearpublic final Buffer clear() 

清除此缓冲区(仅仅将position, limit等重置,不清除数据)

写模式下:调用 clear()方法,切换成读模式,可以从头读取 buffer 的数据

读模式下:将buffer切换为写模式,重置参数,让其从头开始写

写转换成读 flippublic final Buffer flip()写模式转读模式,反转此缓冲区(limit = position, position = 0)
倒带 rewindpublic final Buffer rewind()

position=0, remark=-1,可以让Buffer倒回去重新读

与flip()不同之处在于rewind()不修改limit

是否到底了 hasRemainingpublic final boolean hasRemaining()return position < limit
抽象方法 isReadOnlypublic abstract boolean isReadOnly()是否是只读缓冲区,不同底层实现类返回结果不同。

返回hb[]数组 hasArray,array

委托IntBuffer实现

public abstract boolean hasArray()

public abstract Object array()

是否有可访问的底层实现数组

返回缓冲区的底层实现数组

IntBuffer抽象类方法

分配内存(初始化)allocate

委托HeapIntBuffer实现

public static XXXBuffer allocate(int capacity){

       return new HeapXXXBuffer(capacity, limit)}

抽象类Buffer没有这个方法,实现类基本都有这个静态方法,实际调用调用子类HeapXXXBuffer

eg. IntBuffer.allocate(20);DoubleBuffer.allocate(20)...

写模式 put

委托HeapIntBuffer实现

public IntBuffer put(int x)

写入数据,position++;

allocate(),Buffer.clear(),Buffer.compact()之后,都可以将Buffer转换成写模式

读模式 get

委托HeapIntBuffer实现

public int get()

读position位置的数据,position++

读之后不能直接写数据,一旦读取了所有的 Buffer 数据,那么我们必须清理 Buffer

让其重新可写,可以调用 Buffer.clear() 或 Buffer.compact()。

(4)只读Buffer——>HeapByteBuffer

(5)基本使用步骤

  • 步骤
    • allocate分配Buffer
    • buffer.put() 
    • 调用 Buffer.flip()方法,将 NIO Buffer 转换为读模式
    • buffer.get()
    • 调用 Buffer.clear() 或 Buffer.compact()方法,将 Buffer 转换为写模式
  • 案例
  • public static void main(String[] args){
        //创建一个buffer
        ByteBuffer buffer = ByteBuffer.allocate(64);
        //放入数据
        for(int i = 0; i < 64; i++){
            buffer.put((byte)i);
        }
        //读取
        buffer.flip();
        //得到一个只读的buffer
        ByteBuffer byteBuffer = buffer.asReadOnlyBuffer();
    }

3.2 Channel

和老的Java IO相比,有三点区别

  • 传统Stream相比,前者是单向的。Channel是双向的
  • 传统是读写阻塞的,Channle是异步的
  • Channel总是基于缓冲区Buffer来读写

(1)继承关系

  • public interface Channel extends Closeable
  • 较为常用的类:
    • FileChannel(不支持非阻塞模式) ——> FileChannelImpl:文件数据读写
    • DatagramChannelImpl:UDP数据读写
    • ServerSocketChannel,SocketChannel ——> ServerSocketChannelImpl,SocketChannelImpl:TCP数据读写的Server和Client

(2)方法

方法:FileChannel为例 

public int read(ByteBuffer dst)

public int write(ByteBuffer src)

Channel ——> Buffer

Buffer ——> Channel

public long transferFrom(ReadableByteChannel src, long position, long count)

public long transferTo(long position, long count, WritableByteChannel target)

src Channel ——> 当前Channel

当前Channel ——> target Channel

(3)案例

  • 1. string写入文件
    • public static void main(String[] args) throw Exception{
          String str = "hello, 写入文件案例";
          //写入一个输出流
          FileOutputStream fos = new FileOutputStream("d:\\file.txt");
          //通过FileOutputStream获取对应FileChannel
          //实际上获得的真实类型是FileChannelImpl
          FileChannel fc = fos.getChannel();
          //创建一个缓冲区ByeBuffer
          ByteBuffer bf = ByteBuffer.allocate(1024);
          //将str放入Buffer
          bf.put(str.getBytes());
          //对byteBuffer进行flip
          bf.flip();
          //将buffer————>channel
          fc.write(bf);
          //关闭
          fos.close();
      }
  • 2. 文件写出到string
    • public static void main(String[] args) throw Exception{
          //写入一个输入流
          File file = new File("d:\\file.txt");
          FileInputStream fis = new iFileInputStreaim(file);
          //通过FileInputStream获取对应FileChannel
          //实际上获得的真实类型是FileChannelImpl
          FileChannel fc = fis.getChannel();
          //创建一个缓冲区ByeBuffer
          ByteBuffer bf = ByteBuffer.allocate((int)file.length());
          //将buffer————>channel
          fc.read(bf);
          //获取str
          str = new String(byteBuffer.array());
          //关闭
          fos.close();
      }
  • 3. 利用transform直接拷贝
    • public static void main(String[] args) throw Exception{
          //创建相关流
          FileInputStream fis = new FileInputStream("d:\\a1.jpg");
          FileOutputStream fos = new FileOutputStream("d:\\a2.jpg");
          //获取对应fileChannel
          FileChannel sourceCh = fis.getChannel();
          FileChannel destCh = fos.getChannel();
          //使用transferFrom完成拷贝
          destCh.transferFrom(sourceCh, 0, sourceCh.size());
          //关闭
          sourceCh.close();
          destCh .close();
          fis.close();
          fos.close();
      }

3.3 Selector

(1)传统的服务器设计:利用多线程,每个线程处理一个socket

  • 缺点:
    • 内存占用高
    • 线程上下文切换成本高
    • 只适合连接数少的场景

(2)线程池版优化

  • 缺点:
    • 阻塞模式下,线程仅能处理一个socket连接(eg. socket1执行时,socket被迫阻塞)
    • 仅适合短连接的场景

(3)selector版设计

selector的作用就是配合一个线程来管理多个channel,获取这些channel上发送的事件。适合连接数特别多,但流量低的场景

调用selector的select()会阻塞直到channel发生了读写就绪事件。发生事件,selector就会返回这些事件给thread来处理

https://www.cnblogs.com/crazymakercircle/p/9826883.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值