【基础】

阻塞 非阻塞
线程持续等待资源中数据准备完成,直到返回响应结果。 线程直接返回结果,不会持续等待资源准备数据结束后才响应结果。
异步 同步
异步则指主动请求数据后便可以继续处理其它任务,随后等待IO操作完毕的通知 同步一般指主动请求并等待IO操作完成的方式。

【io与nio区别】

io nio 描述
面向流Stream 面向缓冲Buffer io是面向流的,nio是面向缓冲区的。【面向流】每次从流中读取一个或者多个字节,直至读取完所有的字节,他们没有被缓存在任何地方;也不能前后移动流中的数据【面向缓冲区】数据读取到一个稍后处理的缓冲区,需要时可在缓冲区中前后移动。但是需要检查是否该缓冲区中包含所需要的处理的数据,确保当更多的数据读入缓冲区时,不要覆盖缓冲区里尚未处理的数据。
阻塞IO 非阻塞IO 【io】各种流是阻塞的,当一个线程调用read()或者write()时,该线程被阻塞,直到数据被读取完毕或者写入完毕,该线程才会被释放。【nio】非阻塞模式,使一个线程从某通道发送请求读取数据,但是它仅能得到目前可用的数据,如果通道中没有,就不会获取任何数据。直至数据变的可以读取之前,该线程可以继续做其他的事情,写入也是如此。一个线程请求写入一些数据到某通道,但不需要等待它完全写入,这个线程同时可以去做别的事情。一个单独的线程可以管理多个输入和输出通道
选择器Selectors 【nio】选择器允许一个单独的线程来监视多个输入通道,也就是非阻塞模式,使用一个单独的线程来选择通道,通道里已经有可以处理的输入,或者选择已准备写入的通道。这种选择机制,使得一个单独的线程容易管理多个通道。Java NIO的selectors允许一条线程去监控多个channels的输入,你可以向一个selector上注册多个channel,然后调用selector的select()方法判断是否有新的连接进来或者已经在selector上注册时channel是否有数据进入。selector的机制让一个线程管理多个channel变得简单。

NIO优点:
1、通过通道channel注册到选择器selector上的状态,来实现一种客户端与服务端的通信。通道中的数据的读取是通过Buffer,一种非阻塞的读取方式。
2、Selector多路复用器,单线程模型,线程的资源开销小。

Channel(通道)
io操作对读写read/write方法调用,可能会因为没有数据可读而阻塞,直到数据响应。可能会无限制的阻塞,关键是调用这个方法的时候,无法预知该方法是否会被阻塞。
Buffer(缓冲区)
它是一个缓冲区,实际上是一个容器,一个连续数组。
Channel提供从文件、网络读取数据的渠道,但是读写的数据都必须经过Buffer。

【测试 小demo】
io与nio_第1张图片

    @GetMapping("/0408")
public String getThread(){
    StringBuffer sb = new StringBuffer();
    try {
        InputStream is = new FileInputStream("D:\\Frozen\\testFile\\2020-4-8redant_test.txt");
        BufferedReader reader = new BufferedReader(new InputStreamReader(is,"GBK"));
        sb.append("当前运行的进程 :  " + Thread.currentThread().getName());
        sb.append("
"); sb.append("读取文件的第一行内容为:" + reader.readLine()); sb.append("
"); sb.append("读取文件的第二行内容为:" + reader.readLine()); sb.append("
"); sb.append("读取文件的第三行内容为:" + reader.readLine()); sb.append("
"); sb.append("读取文件的第四行内容为:" + reader.readLine()); sb.append("
"); sb.append("读取文件的第五行内容为:" + reader.readLine()); sb.append("
"); }catch (Exception e){ e.printStackTrace(); } return sb.toString(); }

io与nio_第2张图片

【nio:基于通道 & 缓冲数据的文件复制】

@GetMapping("/0409")
public void baseChannelData(){
    try {
        // 1. 获取数据源 和 目标传输地的输入输出流(此处以数据源 = 文件为例)
        File file = new File("D:\\Frozen\\testFile\\20200409.txt");
        FileInputStream fin = new FileInputStream(file);
        File outfile = new File("D:\\Frozen\\testFile\\20200410.txt");
        FileOutputStream fout = new FileOutputStream(outfile);
        // 2. 获取数据源的输入输出通道
        FileChannel fcin = fin.getChannel();
        FileChannel fcout = fout.getChannel();
        // 3. 创建 缓冲区 使用allocate()静态方法
        ByteBuffer buff = ByteBuffer.allocate(1024);//字节
        //再额外给你塞几个
        StringBuffer sb = new StringBuffer();
        sb.append("★★★★★★★★★★★★★★★★★★★\n\r\t");
        sb.append("★★★★★★★★★★★★★★★★★★★/n/r/t");
        sb.append("2020年4月9日15:14:19");
        sb.append("2020年4月9日15:14:19");
        sb.append("2020年4月9日15:14:19");
        sb.append("2020年4月9日15:14:19");
        sb.append("2020年4月9日15:14:19");
        sb.append("2020年4月9日15:14:19");

        ByteBuffer sendBuff = ByteBuffer.wrap(sb.toString().getBytes("GBK"));
        buff.put(sendBuff);
        // 4. 从通道读取数据 & 写入到缓冲区
        // 注:若 以读取到该通道数据的末尾,则返回-1
        fcin.read(buff);

        // 5. 传出数据准备:将缓存区的写模式 转换->> 读模式
        buff.flip();

        // 6. 从 Buffer 中读取数据 & 传出数据到通道
        fcout.write(buff);
        // 7. 重置缓冲区
        // 目的:重用现在的缓冲区,即 不必为了每次读写都创建新的缓冲区,在再次读取之前要重置缓冲区
        // 注:不会改变缓冲区的数据,只是重置缓冲区的主要索引值
        buff.clear();

    }catch (Exception e){
        e.printStackTrace();
    }

}

io与nio_第3张图片

【nio服务器端如何实现非阻塞】
服务器上所有Channel需要向Selector注册,而Selector负责监视这些Socket的io状态。当其中任意一个或者多个Channel具有可用的io操作时,该选择器的select()方法返回大于0的整数,这个数字表示选择器上有多少个Channel可以操作。