JavaSE-NIO总结


1. 同步和异步

同步和异步站在任务调度者角度:描述消息通信的机制

  1. 同步(Synchronous):调用一个方法后,必须要等待一个返回值才能执行后续操作
  2. 异步(Asynchronous):调用一个方法后,不需要等待返回值就可以执行后续操作,当被调用的方法执行完成后,一般会通过状态、通知和回调来通知调用者

2. 阻塞和非阻塞

阻塞和非阻塞站在CPU角度:描述等待调用结果时的状态

  1. 阻塞:调用结果返回之前,当前线程会被挂起,只有在得到结果之后才会返回
  2. 非阻塞:若该调用不能立刻得到结果,则该调用不会阻塞当前线程,会立刻返回,相应操作在后台继续处理,调用者需要定时轮询查看处理状态

阻塞和非阻塞是同步和异步的一种属性

3. IO类型

  1. BIO(Block IO):同步阻塞式IO;JDK 1.4之前面向流的I/O,任何数据都是以流的形式进行传输,速度较慢
  2. NIO(non-blocking IO):同步非阻塞式IO;在JDK 1.4中出现了NIO,是一个**面向块(缓冲区)**的I/O,任何数据都是以块的形式进行传输;在java.nio包中
  3. NIO2/AIO(Asynchronous IO):异步非阻塞式IO;在JDK 1.7中NIO有了进一步改进,即NIO2/AIO

NIO之所以是同步,是因为它的accept/read/write方法的内核I/O操作都会阻塞当前线程

NIO在并发量特别大时可以使用,一般情况下IO就够用了

4. NIO主要组成

NIO的主要组成部分:Buffer(缓冲区)、Channel(通道)、Selector(选择器)

4.1 Buffer(缓冲区)

  1. NIO中通过Buffer来读写数据,所有数据都用Buffer来处理,Buffer是NIO读写数据的中转池

  2. Buffer对基本类型的数组进行了封装(但排除了boolean),已知直接子类:ByteBuffer、CharBuffer、DoubleBuffer、FloatBuffer、IntBuffer、LongBuffer、ShortBuffer

  3. 缓冲区内部存在指针,初始时在0索引处,对缓冲区进行读写操作时,操作的是指针所指索引处的数据,操作完后指针会后移一次;因此在读之前写的数据时,应让指针重新指向缓冲区开头

  4. ByteBuffer常用方法:

    创建ByteBuffer对象:
    间接缓冲区的创建和销毁效率高于直接缓冲区,但工作效率低于直接缓冲区
    // 方式1:在堆中创建间接缓冲区*
    static ByteBuffer allocate(int capacity)
    // 方式2:在系统内存中创建直接缓冲区
    static ByteBuffer allocateDirect(int capacity)
    // 方式3:通过数组创建间接缓冲区
    static ByteBuffer wrap(byte[] array)
    
    添加数据:不能让缓冲区内部的指针超出容量
    // 向缓冲区当前可用位置添加数据
    abstract ByteBuffer put(byte b)
    // 向缓冲区当前可用位置添加一个byte[]数组
    ByteBuffer put(byte[] src)
    // 向缓冲区当前可用位置添加一个byte[]数组的一部分
    ByteBuffer put(byte[] src, int offset, int length)
    
  5. ByteBuffer直接抽象子类MappedByteBuffer,可以将文件直接映射至内存,把硬盘中的读写变成内存中的读写,因此可以提高大文件的读写效率

    FileChannel的map方法获取MappedByteBuffer// 将节点中从position开始的size个字节映射到返回的MappedByteBuffer中
    // 由源码得知:参数size可以传递的最大值为2147483647L(int的最大值2^31-1),即size<2G
    abstract MappedByteBuffer map(FileChannel.MapMode mode, long position, long size)
    
  6. Buffer常用方法:

    // 返回此缓冲区的底层实现数组
    // 若是直接缓冲区,则会抛出不支持操作异常
    abstract Object array()
    
    // 返回此缓冲区的容量,Buffer创建后容量不可变
    int capacity()
    
    限制只能在[position, limit)区间内读写
    // 获取此缓冲区的限制
    // 初始时limit() = capacity()
    int limit()
    // 设置此缓冲区的限制
    Buffer limit(int newLimit)
    
    position值应在[0, limit]区间内
    // 获取当前可写入位置索引
    int position()
    // 更改当前可写入位置索引
    Buffer position(int newPosition)
    
    // 设置此缓冲区的标记为当前的position
    Buffer mark()
    // 将此缓冲区的位置重置为mark标记的position
    // 若没有标记,则会抛出异常
    Buffer reset()Buffer存入数据后可以flip()一下,便于下次取出
    // limit() = position()
    // position() = 0
    // 丢弃mark()
    Buffer flip()Buffer取出数据后可以clear()一下,便于下次存入
    // limit() = capacity()
    // position() = 0
    // 丢弃mark()
    Buffer clear()
    
    // 重绕此缓冲区:
    // limit()不变
    // position() = 0
    // 丢弃mark()
    Buffer rewind()
    
    // 获取position与limit之间的元素数
    int remaining()
    
    // 获取当前缓冲区是否只读
    abstract boolean isReadOnly()
    
    // 获取当前缓冲区是否为直接缓冲区
    abstract boolean isDirect()
    

4.2 Channel(通道)

  1. Channel是一个双向通道,用于连接I/O操作,可以读、写、同时读写,而IO流只能读或写

  2. Channel常用抽象实现类:

    1. FileChannel:读写文件数据;可以使用FileInputStream或FileOutputStream的getChannel()方法获取
    2. SelectableChannel:可通过Selector实现多路复用的通道;是下面3个的爷爷类
    3. DatagramChannel:读写UDP数据
    4. SocketChannel:读写TCP数据
    5. ServerSocketChannel:监听TCP连接
  3. FileChannel的读写方法:

    // 读取多少字节,position就后移多少,最多和limit一样
    // read方法给ByteBuffer中成功放入数据后,FileChannel中指着文件的指针才会后移
    abstract int read(ByteBuffer dst)
    
    // 将[position, limit)区间内的内容写出,写多少就将position后移多少
    // write方法从ByteBuffer中成功取出数据放入文件后,FileChannel中指着文件的指针才会后移
    abstract int write(ByteBuffer src)
    
  4. 示例1:FileChannel结合MappedByteBuffer复制大于2GB的文件

    import java.io.IOException;
    import java.io.RandomAccessFile;
    import java.nio.MappedByteBuffer;
    import java.nio.channels.FileChannel;
    
    /**
     * FileChannel结合MappedByteBuffer复制大于2GB的文件:
     * 可以将文件分成几块,每块小于2GB,每次映射一块
     * <p>
     * InputStream获取的Channel只能映射为READ_ONLY模式
     * OutputStream获取的Channel不可以映射,并且OutputStream的Channel只能写不能读
     * RandomAccessFile获取的Channel可以映射为READ_ONLY、READ_WRITE、PRIVATE三种模式任意一种
     * java.io.RandomAccessFile是一个可以设置读r写w模式的IO流类
     */
    public class CopyGE2G {
         
        public static void main(String[] args) throws IOException {
         
            String src = "D:\\src.txt";
            String dest = "D:\\dest.txt";
            // 1. 获取流对象
            // 输入流,只需读文件就行了
            RandomAccessFile r = new RandomAccessFile(src, "r");
            // 输出流,需要读和写
            RandomAccessFile rw = new RandomAccessFile(dest, "rw");
    
            // 2. 根据流对象获取Channel对象
            FileChannel rChannel = r.getChannel();
            FileChannel rwChannel = rw.getChannel();
    
            // 获取文件大小
            long fileSize = rChannel.size();
            // 设置每次映射的的大小:此处为512MB
            long mapSize = 512 * 1024 * 1024;
            // 一共需要映射多少次,需要向上取整
            long count = fileSize % mapSize == 0 ? fileSize / mapSize : fileSize / mapSize + 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值