• Java NIO学习笔记


    一、概念
    面向块的非阻塞IO系统。由通道、缓冲和Selector实现,通道传输数据,缓冲暂存和操作数据,Selector支持单线程操作多缓冲
    (1)优势:
    • NIO有缓冲功能,通过使用map方法可以直接将“一块数据”映射到内存中,比较高效。FileChannel的map方法返回MappedByteBuffer对象,将磁盘文件的部分或全部内容映射到内存中
    • NIO提供了支持非阻塞式IO的Selector类
    • NIO提供了将unicode字符串映射成字节序列及反映射的Charset类
    二、相关包
    java.nio Buffer 相关的类
    java.nio.channels Channel和Selector相关的类
    java.nio.charset 字符集相关的类
    java.nio.channels.spi 提供Channel服务相关的类
    java.nio.charset.spi 提供字符集服务相关的类
    三、Buffer(缓冲)
    重要子类
    ByteBuffer MappedByteBuffer
    CharBuffer
    ShortBuffer
    IntBuffer
    LongBuffer
    FloatBuffer
    DoubleBuffer
    本质是一个数组,程序用来操作数据 0<=mark<=position<=limit<=capacity
    • 容量(capacity):最大数据容量。创建后不可改变
    • 界限(limit):位于limit后的数据不可被读写
    • 位置(position):下一个可被读写的缓冲区位置索引
    • 标记(mark):允许将postion直接定位至此处

    (1)方法
    int capacity() 返回capacity的大小
    int limit() 返回limit的大小
    int postition() 返回position的大小
    Buffer mark() 在当前postition处设置mark
    Buffer limit(int newLt) 返回一个重新设置limit值的新Buffer
    Buffer postition(int newPs) 返回一个重新设置postition值的新Buffer
    Buffer reset() 将postition转到mark所在的位置
    Buffer rewind() postition置0,并取消mark,用于重读数据
    boolean hasRemain() 判断position和limit间是否还有可供处理的元素
    intt remaining() postition和limit间的元素个数
    Buffer flip() limt=position,position=0,丢弃标记,用于写切换到读
    Buffer clear() limit=capacity,posititon=0,丢弃标记,用于读切换到写
    使用Buffer读写数据的一般步骤:
    • 写入数据到Buffer
    • 调用flip()方法
    • 从Buffer中读取数据
    • 调用clear()方法或者compact()方法
    (2)Buffer的所有子类的额外方法
    • 读写
    Xxx get() 返回position位置的数据,position递增 相对
    XxxBuffer put(xxx c) 写入数据到position,position递增 相对
    Xxx get(index i) 返回i位置的数据,position不变 绝对
    XxxBuffer put(xxx c) 写入数据到i位置,position不变 绝对
    • static XxxBuffer allocate(int capacity) 创建普通Buffer对象
    o ByteBuffer特有的创建直接Buffer对象的方法:static ByteBuffer allocateDirect(int capacity) 更高效,绕过JVM
    • XxxBuffer compact()
    o 压缩缓冲区,将position和limit间的数据复制到缓冲区开始处,重置position为复制的字节数,limit为capacity
    boolean equals(Object ob) 判断此缓冲区是否与另一个对象相同。必须同时满足:(1)具有相同的元素类型和变量类型(2)具有相同数量的剩余元素(3)剩余元素序列(与它们的起始位置无关)逐点相同
    int compareTo(XxxBuffer that) 比较同类型的缓冲区。A小于B必须满足:(1)A第一个不相等的元素小于B中对应的元素(2)所有元素都相等,但A比B先耗尽
    (3)示例
    • 创建Buffer
    o ByteBuffer buf = CharBuffer.allocate(1024);
    • 向Buffer写数据,两种方法
    o int bytesRead = inChannel.read(buf);
    o buf.put((byte)127);
    • 从Buffer读数据,两种方法
    o int bytesWritten = inChannel.write(buf);
    o byte aByte = buf.get();
    四、Channel(通道)
    FileChannel 从文件中读写数据
    DatagramChannel 通过UDP读写网络中的数据
    SocketChannel 通过TCP读写网络中的数据
    ServerSocketChannel 监听新进来的TCP连接,像Web服务器那样。对每一个新进来的连接都会创建一个SocketChannel。
    Channel是一个java.nio.channels的接口,系统为该接口提供了各种实现类。全双工,比流能更好映射底层OS的API,特别是UNIX网络编程模型中底层OS的通道是全双工的
    4.1 与流的区别:
    • 程序不能直接读取Channel中的数据,需要通过Buffer对象访问。
    • 通道既能读数据,也能写数据,是双向的,而流是单向的。
    • 通道可以异步读写
    传统的节点流的getChannel方法可以创建对应的Channel对象,但SocketChannel由SocketChannel.open()创建,再使用connect方法连接。
    4.2 方法:
    MappedByteBuffer map(FileChannel.MapMode mode,long position, long size) 第一个参数是映射模式。用于实现NIO的块功能 FileChannel
    int read(ByteBuffer dst) 将字节序列从此通道中读入给定的缓冲区 SocketChannel、FileChannel
    long read(ByteBuffer[] dsts) 将字节序列从此通道scatter分发到给定的缓冲组 SocketChannel、FileChannel
    long read(ByteBuffer[] dsts, int offset, int length) 将字节序列从此通道读入给定缓冲组的子序列中 SocketChannel、FileChannel
    int write(ByteBuffer src) 将字节序列从给定的缓冲组中写入此通道 SocketChannel、FileChannel
    long write(ByteBuffer[] srcs) 将字节序列从给定的缓冲组gather聚集写入此通道 SocketChannel、FileChannel
    long write(ByteBuffer[] srcs, int offset, int length) 将字节序列从给定缓冲组的子序列写入此通道 SocketChannel、FileChannel
    long transferFrom(ReadableByteChannel src,
    long position, long count) 将字节从给定的可读取字节通道传输到此FileChannel通道的文件中 FileChannel
    long transferTo(long position,
    long count, WritableByteChannel target) 将字节从FileChannel通道的文件传输到给定的可写入字节通道 FileChannel
    FileChannel truncate(long size) 截取文件时,文件将中指定长度后面的部分将被删除 FileChannel
    void force(boolean metaData) 将通道里尚未写入磁盘的数据强制写到磁盘上,metaData指明是否同时将文件元数据(权限信息等)写到磁盘上 FileChannel
    4.3 常用Channel示例
    (1)FileChannel
    文件通道,不能运行在非阻塞模式下,不能注册到Selector
    • 打开FileChannel
    o FileChannel inChannel=new RandomAccessFile(f, "rw").getChannel();
    • 从FileChannel读取数据,两种方法
    o MappedByteBuffer mbb=inChannel.map(FileChannel.MapMode.READ_ONLY, 0, f.length() );
    o 或ByteBuffer bb=ByteBuffer.allocate((int)f.length());
    int buffer=inChannel.read(bb);
    • 将读取的字节序列解码为GBK字符集的字符序列并输出,用Charset类
    o Charset charset=Charset.forName("GBK");
    CharsetDecoder decoder=charset.newDecoder();
    CharBuffer charBuffer=decoder.decode(bb);
    System.out.println(charBuffer);
    • 向FileChannel写入数据
    o bb.clear();
    bb.put(newData.getBytes());
    bb.flip();
    while(bb.hasRemaining()) {
    inChannel.write(bb);
    }
    五、Selector
    多路复用器
    允许单线程处理多个Channel,提供选择已经就绪的任务的能力,实现非阻塞的核心代码。可用来实现非阻塞式socket通信。
    要使用Selector,得向Selector注册Channel,然后调用它的select()方法。这个方法会一直阻塞到某个注册的通道有事件就绪。一旦这个方法返回,线程就可以处理这些事件,事件的例子有如新连接进来,数据接收等。
    一个多路复用品可以同时轮询多个Channel,由于JDK使用了epoll()代替了传统的select实现,没有最大连接句柄1024/2048的限制,只需一个线程负责Selector的轮询,就可以接入成千上万的客户端

    (1)Selector类
    static Selector open() 打开一个选择器
    int select() 阻塞到至少有一个通道在你注册的事件上就绪,返回自上次调用select()方法后有多少通道变成就绪状态
    int select(long timeout) 阻塞到至少有一个通道在你注册的事件上就绪,最长会阻塞timeout毫秒
    int selectNow() 不会阻塞,不管什么通道就绪都立刻返回,则无就绪则返回0
    Set<SelectionKey> selectedKeys() 返回此选择器的已选择键集
    Selector wakeUp() 使尚未返回的第一个选择操作立即返回
    void close() 关闭此选择器
    (2)SelectableChannel抽象类
    SelectionKey register(Selector sel,int ops,Object att) 注册时关联附加对象
    SelectionKey register(Selector sel,int ops) 向指定的选择器注册通道,并返回选择键
    SelectableChannel configureBlocking(boolean block) 设置是否阻塞
    ServerSocketChannel是子类,额外的方法有
    static ServerSocketChannel open() 打开socket通道
    ServerSocket socket() 返回与此通道关联的socket
    ServerSocketChannel bind(SocketAddress local) 绑定
    SocketChannel accept() 接受连接并返回SocketChannel对象
    (3)SelectionKey类,连接通道、选择器
    int interestOps() 获取此键的 interest 集合
    int interestOps(int ops) 将此键的 interest 集合设置为给定值
    int readyOps() 获取此键的 ready 操作集合
    boolean isConnectable() 测试此键的通道是否已完成其套接字连接操作
    boolean isAcceptable() 测试此键的通道是否已准备好接受新的套接字连接
    boolean isReadable() 测试此键的通道是否已准备好进行读取
    boolean isWritable() 测试此键的通道是否已准备好进行写入
    Selectable channel() 返回创建此键的通道
    Selector selector() 返回此键对应的Selector
    Object attach(Object ob) 将给定的对象附加到此键,方便识别
    Object attachment() 获取当前的附加对象
    void cancel() 请求取消此键的通道到其选择器的注册
    5.1 创建
    Selector selector=Selector.open();
    5.2 注册通道
    通道必须处于非阻塞模式,才能与Selector一起使用。因此FileChannel不能与Selector一起使用,SocketChannel可以。
    channel.configureBlocking(false);
    SelectionKey key = channel.register(selector,Selectionkey.OP_READ);
    (1)register()是SelectableChannel抽象类定义的方法
    • 第二个参数是“interest集合”,即Selector监听Channel时对什么事件就绪感兴趣,可以监听以下四种事件。用按位或操作设置对多个事件感兴趣,如int interestSet = SelectionKey.OP_READ | SelectionKey.OP_WRITE。用按位与操作获取某个事件是否在特定的interest集合里,如boolean isInterestedInConnect = interestSet & SelectionKey.OP_CONNECT;
    事件     对应SelectionKey的常量      意义
    Connect   SelectionKey.OP_CONNECT   客户端连接服务端事件
    Accept    SelectionKey.OP_ACCEPT       服务端接收客户端连接事件
    Read     SelectionKey.OP_READ        读事件
    Write      SelectionKey.OP_WRITE     写事件
    5.3 通过Selector选择通道
    Set selectedKeys = selector.selectedKeys();
    Iterator keyIterator = selectedKeys.iterator(); //用Iterator迭代器对Set对象进行hasNext()、next()等操作
    for(SelectionKey sk : selectedKeys); //或者用foreach迭代
    if(sk.isAcceptable()){...}
    六、编码集和Charset
    (1)编码Encode 解码Decode

    (2)Charset类的方法
    static SortedMap<String,Charset> availableCharsets() 获取当前JDK支持的所有字符集
    static Charset forName(String charsetName) 创建指定字符集的Charset对象
    CharsetEncoder newEncoder() 创建编码器
    CharsetDecoder newDecoder() 创建解码器
    ByteBuffer encode(CharBuffer cb) 字符序列编码为字节序列
    ByteBuffer encode(String st) 字符序列编码为字节序列
    CharBuffer decode(ByteBuffer bb) 字节序列解码为字符序列
    (3)获取系统的字符集属性:
    Properties props=System.getProperties();
    props.getProperty("file.encoding");
    (4)获取文件的字符集属性(通过文件的前两个字节进行判断):
    BufferedInputStream bin = new BufferedInputStream(new FileInputStream(fileName));
    int p = (bin.read() << 8) + bin.read(); 或 int[] p=new int[2]; bin.read(p);
    对p进行判断,
    0xefbb UTF-8
    0xfffe Unicode
    0xfeff UTF-16BE
    0x5c75 ANSI|ASCII
    default GBK
    七、文件锁
    (1)FileChannel对象的获得FileLock对象的方法
    lock() 阻塞式,得到整个文件锁前都会阻塞,排他锁
    tryLock() 直接返回,得到整个文件锁返回文件锁,否则返回null,排他锁
    lock(long position, long size, boolean shared) 阻塞式,得到文件锁前都会阻塞
    tryLock(long position, long size, boolean shared) 直接返回,得到文件锁返回文件锁,否则返回null
    (2)FileLock对象的方法
    release() 释放锁
    isShared() 判断是否共享锁
    size() 返回被锁区域的大小

  • 相关阅读:
    面向过程编程
    生成器
    迭代器
    装饰器
    函数对象与闭包
    名称空间和作用域
    Django中的as_view方法源码分析
    DRF3序列化反序列化
    DRF4级联与外键字段
    django--BBS项目,后端业务逻辑整理
  • 原文地址:https://www.cnblogs.com/codetouse/p/6268110.html
Copyright © 2020-2023  润新知