您当前的位置:首页 > 电脑百科 > 程序开发 > 语言 > JAVA

什么是NIO?什么是BIO?NIO 和 BIO 有什么区别?

时间:2021-06-03 11:03:40  来源:今日头条  作者:摸鱼一线码农

一、什么是NIO

1.概念

NIO是JAVA1.4中引入的,被称为new I/O,也有说是non-blocking I/O,NIO被成为同步非阻塞的IO。

 

2.跟BIO流的区别

  1. BIO是面向流的,NIO是面向块(缓冲区)的
  2. BIO的流都是同步阻塞的,而NIO同步非阻塞的
  3. NIO会等待数据全部传输过来再让线程处理,BIO是直接让线程等待。
  4. NIO有选择器,而BIO没有。
  5. NIO是采用管道和缓存区的形式来处理数据的,而BIO是采用输入输出流来处理的。
  6. NIO是可以双向的,BIO只能够单向。

 

二、NIO常用组件Channel和Buffer的使用

1.代码

这里以文件复制为例

public class test {
    public static void main(String[] args){
        try{
            //存在的照片
            File inFile=new File("C:\Users\Administrator\Desktop\study.PNG");
            //复制后要存放照片的地址
            File outFile=new File("C:\Users\Administrator\Desktop\study1.PNG");
            //打开流
            FileInputStream fileInputStream=new FileInputStream(inFile);
            FileOutputStream fileOutputStream=new FileOutputStream(outFile);
            /**
             * RandomaccessFile accessFile=new RandomAccessFile(inFile,"wr");
             *  FileChannel inFileChannel=accessFile.getChannel();
             *  和下面两行代码是一样的,都是可以拿到FileChannel
             */
            //获取Channel
            FileChannel inFileChannel=fileInputStream.getChannel();
            FileChannel outFileChannel=fileOutputStream.getChannel();
		   //创建buffer
            ByteBuffer buffer=ByteBuffer.allocate(1024*1024);
			//读取到buffer中
            while (inFileChannel.read(buffer)!=-1){
                //翻转一下,就可以读取到全部数据了
                buffer.flip();
                outFileChannel.write(buffer);
                //读取完后要clear
                buffer.clear();
            }
            //关闭
            inFileChannel.close();
            outFileChannel.close();
            fileInputStream.close();
            fileOutputStream.close();
        }catch (Exception e){}

    }
}
复制代码

我的桌面上的确多了一张一模一样的图片

2.解释

使用NIO的话,需要注意几个步骤:

  1. 打开流
  2. 获取通道
  3. 创建Buffer
  4. 切换到读模式 buffer.flip()
  5. 切换到写模式 buffer.clear(); 其实这里也看不出来它是怎么使用缓冲区的,上面这段代码中的while循环的作用和下面的代码是一样的
 while ((i=fileInputStream.read())!=-1){
                fileOutputStream.write(i);
          }

让我们赶紧开始NIO的编程

三、BIO和NIO的区别

学习了Channel和Buffer的使用,我们就可以正式进入NIO的开发了

代码

NIO

NIO服务端:只是接受客户端发送过来的数据,然后打印在控制台

/**
 * NIO
 * @author xuxiaobai
 */
public class NIOTest {
    private final static int port = 8080;

    public static void main(String[] args) throws IOException {
        //启动服务端
        TCPServer();
    } 
	/**
     * TCP服务端
     * 接受TCP
     *
     * @throws IOException
     */
    public static void TCPServer() throws IOException {
        //创建服务端多路复用选择器
        Selector selector = Selector.open();
        //创建服务端SocketChannel
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        //定义地址
        InetSocketAddress inetSocketAddress = new InetSocketAddress(InetAddress.getLocalHost(), port);
        //绑定地址
        serverSocketChannel.bind(inetSocketAddress);
        System.out.println("绑定成功:" + inetSocketAddress);
        //设置为非阻塞
        serverSocketChannel.configureBlocking(false);
        //注册服务端选择端,只接受accept事件
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        while (true) {
            //加上延时,什么原理我忘记了,只知道是为了防止死锁
            selector.select(500);
            //遍历服务端选择器的事件
            Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
            while (iterator.hasNext()) {
                SelectionKey next = iterator.next();
                if (!next.isValid()) {
                    //该key无效直接跳过
                    continue;
                }
                //注意
                if (next.isAcceptable()) {
                    //1. accept事件
                    //接收到accept事件,拿到channel,这个是服务端SocketChannel
                    ServerSocketChannel channel = (ServerSocketChannel) next.channel();
                    //accept得到连接客户端的channel
                    SocketChannel accept = channel.accept();
                    accept.configureBlocking(false);
                    //注册write事件
                    accept.register(selector, SelectionKey.OP_READ);
                    iterator.remove();
                } else if (next.isReadable()) {
                    //2. read事件
                    //开启一个新的线程
                    Thread thread = new Thread(() -> {
                        SocketChannel channel = (SocketChannel) next.channel();
                        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
                        byteBuffer.clear();
                        try {
                            channel.read(byteBuffer);
                            //开始处理数据
                            byteBuffer.flip();
                            byte[] bytes = new byte[byteBuffer.remaining()];
                            byteBuffer.get(bytes);
                            String x = new String(bytes);
                            if(x.equals("")){
                                //老是会莫名其妙地打印一些空行,打个补丁
                                return;
                            }
                            System.out.println(x);
                            if ("exit".equals(x)) {
                                //关闭通道
                                try {
                                    channel.close();
                                } catch (IOException e) {
                                    e.printStackTrace();
                                }
                                next.cancel();
                            }
                        } catch (IOException e) {
                            //出现异常的处理
                            e.printStackTrace();
                            try {
                                channel.close();
                            } catch (IOException ioe) {
                                ioe.printStackTrace();
                            }
                            next.cancel();
                        }

                    });
                    iterator.remove();
                    thread.start();
                }
            }
        }
    }
}

BIO

BIO服务端:接受客户端的数据,然后打印在控制台

BIO客户端:向服务端发送数据。NIO的测试中也使用这个客户端进行测试

/**
 * BIO
 * @author xuxiaobai
 */
public class BIOTest {
    private final static int port = 8080;

    public static void main(String[] args) throws IOException {
        TCPClient();
//        TCPServer();
    }

    /**
     * TCP客户端
     * 发送TCP
     * @throws IOException
     */
    private static void TCPClient() throws IOException {
        SocketChannel socketChannel = SocketChannel.open();
        //定义地址
        InetSocketAddress inetSocketAddress = new InetSocketAddress(InetAddress.getLocalHost(), port);
        //连接
        socketChannel.connect(inetSocketAddress);
        System.out.println("连接成功:"+inetSocketAddress);
        Scanner scanner = new Scanner(System.in);
        while (true) {
            String next = scanner.next();
            //直接包装一个buffer
            ByteBuffer wrap = ByteBuffer.wrap(next.getBytes());
            //写入
            socketChannel.write(wrap);
            if ("exit".equals(next)) {
                //等于exit时关闭channel
                socketChannel.close();
                break;
            }
        }
    }

    /**
     * TCP服务端
     * 接受TCP
     * @throws IOException
     */
    private static void TCPServer() throws IOException {
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        //定义地址
        InetSocketAddress inetSocketAddress = new InetSocketAddress(InetAddress.getLocalHost(), port);
        //绑定
        serverSocketChannel.bind(inetSocketAddress);
        System.out.println("绑定成功:"+inetSocketAddress);
        while (true) {
            //接受连接
            SocketChannel accept = serverSocketChannel.accept();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    //定义一个缓冲区,读出来的数据超出缓冲区的大小时会被丢弃
                    ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
                    while (true) {
                        try {
                            //每次使用前都要清空,但这里没有真的区clear数据,只是移动了buffer里面的下标
                            byteBuffer.clear();
                            //读取数据到缓冲区
                            accept.read(byteBuffer);
                            //每次读取数据前都要flip一下,这里都移动下标
                            byteBuffer.flip();
                            byte[] bytes = new byte[byteBuffer.remaining()];
                            //获取数据
                            byteBuffer.get(bytes);
                            String x = new String(bytes);
                            System.out.println(x);
                            if (x.equals("exit")) {
                                //当读出来的数据等于exit时退出
                                accept.close();
                                break;
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
            }).start();
            //启动该线程
        }
    }
}

搞完了代码,让我们来看看代码的演示效果————从客户端发送数据到服务端,下面展示一下效果:

先后启动BIO的TCPServer和TCPClient方法;

TCPClient:

Java:什么是NIO?什么是BIO?NIO 和 BIO 有什么区别?

 

TCPServer:

Java:什么是NIO?什么是BIO?NIO 和 BIO 有什么区别?

 

步骤

Java:什么是NIO?什么是BIO?NIO 和 BIO 有什么区别?

 

画了个图来表示,这是关于selector的配置流程,在循环中根据不同key值所进行的操作,跟上面文件复制的例子差不多了,只不过这里的Channel是通过 key.channel()获得的。

 

差别

我们来看看一下BIO和NIO的差别。

BIO

我们用IDEA的debug启动BIO的服务端,然后在启动多个客户端。

Java:什么是NIO?什么是BIO?NIO 和 BIO 有什么区别?

 

我这里启动了三个客户端,可以看到有三个线程已经创建好了,然而我这时还没有发送数据到服务端。

NIO

我们用IDEA的debug启动NIO的服务端,然后在启动多个BIO客户端。

Java:什么是NIO?什么是BIO?NIO 和 BIO 有什么区别?

 

这里启动了多个客户端,服务器上没有多余的几个线程。

修改BIO的TCPClient方法

    private static void TCPClient() throws IOException {
        SocketChannel socketChannel = SocketChannel.open();
        //定义地址
        InetSocketAddress inetSocketAddress = new InetSocketAddress(InetAddress.getLocalHost(), port);
        //连接
        socketChannel.connect(inetSocketAddress);
        System.out.println("连接成功:" + inetSocketAddress);
        Scanner scanner = new Scanner(System.in);
        while (true) {
            String next = scanner.next();
            //直接包装一个buffer
//            ByteBuffer wrap = ByteBuffer.wrap(next.getBytes());
            //写入
            while (true) {
                try {
                    //休眠
                  	//注意,休眠时间建议调高一点
                    Thread.sleep(1500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                socketChannel.write(ByteBuffer.wrap(next.getBytes()));
            }
//            if ("exit".equals(next)) {
//                //等于exit时关闭channel
//                socketChannel.close();
//                break;
//            }
        }
    }
复制代码

休眠时间记得调高点!!!宕机警告!

这样客户端就会在读取到第一次时,一直发送这个数据,可以看到一些线程,也是只有在收到数据之后才会创建这个线程去打印这个数据。如果休眠时间调高一点的话,就会看到有时候这里会一闪一闪的,调低后就会出现一闪而过的很多线程,如下图。

Java:什么是NIO?什么是BIO?NIO 和 BIO 有什么区别?

 

四、总结

BIO的话,每次网络请求过来之后,服务器都是会为这个请求创建一个线程,这个线程会一直等待这个请求后续的数据,等处理完成后才会销毁这个线程;而NIO,当每次网络请求过来时,服务器不会马上创建一个线程去处理这个请求,而是会交给一个Selector线程,只有这个请求后续的数据全部传输过来后,Selector才会去通知其他其他线程或者创建一个线程来处理这个请求。

这就是NIO和BIO最大的差别,只有数据传输到服务器时才会让线程去处理,减少了线程的空等待,大部分情况下可以采用线程池的方式来处理数据,可以提高线程的利用率。



Tags:NIO   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
本系列为 Netty 学习笔记,本篇介绍总结Java NIO 网络编程。Netty 作为一个异步的、事件驱动的网络应用程序框架,也是基于NIO的客户、服务器端的编程框架。其对 Java NIO 底层...【详细内容】
2021-12-07  Tags: NIO  点击:(16)  评论:(0)  加入收藏
前言Redis 为何能支持高并发?Redis底层采用NIO中的多路IO复用的机制,对多个不同的连接(TCP)实现IO复用,很好地支持高并发,并且能实现线程安全。Redis官方没有windows版本,只有Linux...【详细内容】
2021-08-12  Tags: NIO  点击:(68)  评论:(0)  加入收藏
一、什么是NIO1.概念NIO是java1.4中引入的,被称为new I/O,也有说是non-blocking I/O,NIO被成为同步非阻塞的IO。 2.跟BIO流的区别 BIO是面向流的,NIO是面向块(缓冲区)的。 BIO的流...【详细内容】
2021-06-03  Tags: NIO  点击:(154)  评论:(0)  加入收藏
一、准备机器  1、准备四台机器,(minio集群最少四台)。192.168.223.132 minio-1192.168.223.133 minio-2192.168.223.134 minio-3192.168.223.135 minio-4  2、编辑hosts文...【详细内容】
2021-05-19  Tags: NIO  点击:(191)  评论:(0)  加入收藏
NIO的三大核心配件 channel(通道)Buffer(缓冲区)selector(选择器) 案例介绍读写切换 public static void main(String[] args) { //创建一个Buffer,大小为5,可就是可以存放...【详细内容】
2021-04-16  Tags: NIO  点击:(304)  评论:(0)  加入收藏
一,NIO新特性 NIO的一些新特性有:非阻塞I/O,选择器,缓冲以及管道。管道(Channel),缓冲(Buffer) ,选择器( Selector)是其主要特征。二,Channel、Selector解释 Channel&mdash;&mdash;管道实...【详细内容】
2021-03-11  Tags: NIO  点击:(298)  评论:(0)  加入收藏
洋葱架构从六边形架构发展而来,在中间的核心业务逻辑里又引入了分层。对于洋葱架构来说,应用构建在领域模型上,里层定义接口,外层实现接口,只能外层调用里层,里层不能调用外层,并且...【详细内容】
2021-02-23  Tags: NIO  点击:(787)  评论:(0)  加入收藏
IO是计算机中Input和Output简称,即输入和输出。 无论是系统、还是语言的设计中IO的设计都是异常复杂的。Java语言在IO设计方面是比较成功的,不仅是面向对象,而且利用装饰器设计...【详细内容】
2020-09-16  Tags: NIO  点击:(42)  评论:(0)  加入收藏
在Java 7,AsynchronousFileChannel 被添加到了Java NIO中。使用AsynchronousFileChannel可以实现异步地读取和写入文件数据。创建一个AsynchronousFileChannel我们可以使用As...【详细内容】
2020-08-06  Tags: NIO  点击:(49)  评论:(0)  加入收藏
要说今年最火的 Air Jordan 鞋款,Dior x Air Jordan 1 自然是稳坐榜首,低调奢华的配色也成为今年大火配色。近日,Jordan Brand 再次推出一款全新配色 Air Jordan 1 “Light Sm...【详细内容】
2020-06-29  Tags: NIO  点击:(138)  评论:(0)  加入收藏
▌简易百科推荐
一、Redis使用过程中一些小的注意点1、不要把Redis当成数据库来使用二、Arrays.asList常见失误需求:把数组转成list集合去处理。方法:Arrays.asList 或者 Java8的stream流式处...【详细内容】
2021-12-27  CF07    Tags:Java   点击:(3)  评论:(0)  加入收藏
文章目录 如何理解面向对象编程? JDK 和 JRE 有什么区别? 如何理解Java中封装,继承、多态特性? 如何理解Java中的字节码对象? 你是如何理解Java中的泛型的? 说说泛型应用...【详细内容】
2021-12-24  Java架构师之路    Tags:JAVA   点击:(5)  评论:(0)  加入收藏
大家好!我是老码农,一个喜欢技术、爱分享的同学,从今天开始和大家持续分享JVM调优方面的经验。JVM调优是个大话题,涉及的知识点很庞大 Java内存模型 垃圾回收机制 各种工具使用 ...【详细内容】
2021-12-23  小码匠和老码农    Tags:JVM调优   点击:(11)  评论:(0)  加入收藏
前言JDBC访问Postgresql的jsonb类型字段当然可以使用Postgresql jdbc驱动中提供的PGobject,但是这样在需要兼容多种数据库的系统开发中显得不那么通用,需要特殊处理。本文介绍...【详细内容】
2021-12-23  dingle    Tags:JDBC   点击:(12)  评论:(0)  加入收藏
Java与Lua相互调用案例比较少,因此项目使用需要做详细的性能测试,本内容只做粗略测试。目前已完成初版Lua-Java调用框架开发,后期有时间准备把框架进行抽象,并开源出来,感兴趣的...【详细内容】
2021-12-23  JAVA小白    Tags:Java   点击:(10)  评论:(0)  加入收藏
Java从版本5开始,在 java.util.concurrent.locks包内给我们提供了除了synchronized关键字以外的几个新的锁功能的实现,ReentrantLock就是其中的一个。但是这并不意味着我们可...【详细内容】
2021-12-17  小西学JAVA    Tags:JAVA并发   点击:(10)  评论:(0)  加入收藏
一、概述final是Java关键字中最常见之一,表示“最终的,不可更改”之意,在Java中也正是这个意思。有final修饰的内容,就会变得与众不同,它们会变成终极存在,其内容成为固定的存在。...【详细内容】
2021-12-15  唯一浩哥    Tags:Java基础   点击:(14)  评论:(0)  加入收藏
1、问题描述关于java中的日志管理logback,去年写过关于logback介绍的文章,这次项目中又优化了下,记录下,希望能帮到需要的朋友。2、解决方案这次其实是碰到了一个问题,一般的情况...【详细内容】
2021-12-15  软件老王    Tags:logback   点击:(17)  评论:(0)  加入收藏
本篇文章我们以AtomicInteger为例子,主要讲解下CAS(Compare And Swap)功能是如何在AtomicInteger中使用的,以及提供CAS功能的Unsafe对象。我们先从一个例子开始吧。假设现在我们...【详细内容】
2021-12-14  小西学JAVA    Tags:JAVA   点击:(21)  评论:(0)  加入收藏
一、概述观察者模式,又可以称之为发布-订阅模式,观察者,顾名思义,就是一个监听者,类似监听器的存在,一旦被观察/监听的目标发生的情况,就会被监听者发现,这么想来目标发生情况到观察...【详细内容】
2021-12-13  唯一浩哥    Tags:Java   点击:(16)  评论:(0)  加入收藏
最新更新
栏目热门
栏目头条