Java NIO 与 IO之间的区别
概述
- Channels and Buffers(通道和缓冲区):标准的IO基于字节流和字符流进行操作的,而NIO是基于通道(Channel)和缓冲区(Buffer)进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。
- Asynchronous IO(异步IO):Java NIO可以让你异步的使用IO,例如:当线程从通道读取数据到缓冲区时,线程还是可以进行其他事情。当数据被写入到缓冲区时,线程可以继续处理它。从缓冲区写入通道也类似。
- Selectors(选择器):Java NIO引入了选择器的概念,选择器用于监听多个通道的事件(比如:连接打开,数据到达)。因此,单个的线程可以监听多个数据通道。
使用场景
NIO
- 优势在于一个线程管理多个通道;但是数据的处理将会变得复杂;
- 如果需要管理同时打开的成千上万个连接,这些连接每次只是发送少量的数据,采用这种;
传统的IO
- 适用于一个线程管理一个通道的情况;因为其中的流数据的读取是阻塞的;
- 如果需要管理同时打开不太多的连接,这些连接会发送大量的数据;
NIO vs IO区别
- IO是面向流的,NIO是面向缓冲区的
- Java IO面向流意味着每次从流中读一个或多个字节,直至读取所有字节,它们没有被缓存在任何地方;
- NIO则能前后移动流中的数据,因为是面向缓冲区的
- IO流是阻塞的,NIO流是不阻塞的
- Java IO的各种流是阻塞的。这意味着,当一个线程调用read() 或 write()时,该线程被阻塞,直到有一些数据被读取,或数据完全写入。该线程在此期间不能再干任何事情了
- Java NIO的非阻塞模式,使一个线程从某通道发送请求读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取。NIO可让您只使用一个(或几个)单线程管理多个通道(网络连接或文件),但付出的代价是解析数据可能会比从一个阻塞流中读取数据更复杂。
- 非阻塞写也是如此。一个线程请求写入一些数据到某通道,但不需要等待它完全写入,这个线程同时可以去做别的事情。
- 选择器
- Java NIO的选择器允许一个单独的线程来监视多个输入通道,你可以注册多个通道使用一个选择器,然后使用一个单独的线程来“选择”通道:这些通道里已经有可以处理的输入,或者选择已准备写入的通道。这种选择机制,使得一个单独的线程很容易来管理多个通道。
Java NIO 由以下几个核心部分组成:
- Channels
- Buffers
- Selectors
Channel
Channel的实现: (涵盖了UDP 和 TCP 网络IO,以及文件IO)
- FileChannel
- DatagramChannel
- SocketChannel
- ServerSocketChannel
读数据:
- int bytesRead = inChannel.read(buf);
写数据:
- int bytesWritten = inChannel.write(buf);
Buffer
Buffer实现: (byte, char、short, int, long, float, double )
- ByteBuffer
- CharBuffer
- DoubleBuffer
- FloatBuffer
- IntBuffer
- LongBuffer
- ShortBuffer
Buffer使用
- flip()方法
- 将Buffer从写模式切换到读模式
- 调用flip()方法会将position设回0,并将limit设置成之前position的值。
- buf.flip();
- (char) buf.get()
- 读取数据
- Buffer.rewind()Buffer.mark()方法,可以标记Buffer中的一个特定position。之后可以通过调用
- 将position设回0,所以你可以重读Buffer中的所有数据
- limit保持不变,仍然表示能从Buffer中读取多少个元素(byte、char等)
- Buffer.reset()方法,恢复到Buffer.mark()标记时的position
- 一旦读完了所有的数据,就需要清空缓冲区,让它可以再次被写入。
- clear()方法会:
- 清空整个缓冲区。
- position将被设回0,limit被设置成 capacity的值
- compact()方法:
- 只会清除已经读过的数据;任何未读的数据都被移到缓冲区的起始处,新写入的数据将放到缓冲区未读数据的后面。
- 将position设到最后一个未读元素正后面,limit被设置成 capacity的值
- buf.put(127);
Buffer的三个属性
- capacity:含义与模式无关;Buffer的一个固定的大小值;Buffer满了需要将其清空才能再写;
- ByteBuffer.allocate(48);该buffer的capacity为48byte
- CharBuffer.allocate(1024);该buffer的capacity为1024个char
- position:含义取决于Buffer处在读模式还是写模式(初始值为0,写或者读操作的当前位置)
- 写数据时,初始的position值为0;其值最大可为capacity-1
- 将Buffer从写模式切换到读模式,position会被重置为0
- limit:含义取决于Buffer处在读模式还是写模式(写limit=capacity;读limit等于最多可以读取到的数据)
- 写模式下,limit等于Buffer的capacity
- 切换Buffer到读模式时, limit表示你最多能读到多少数据;
Selector
概述
Selector允许单线程处理多个 Channel。如果你的应用打开了多个连接(通道),但每个连接的流量都很低,使用Selector就会很方便。例如,在一个聊天服务器中。
使用
- 创建:Selector selector = Selector.open();
- 注册通道:
- channel.configureBlocking(false);
- //与Selector一起使用时,Channel必须处于非阻塞模式
- //这意味着不能将FileChannel与Selector一起使用,因为FileChannel不能切换到非阻塞模式(而套接字通道都可以)
- SelectionKey key = channel.register(selector, Selectionkey.OP_READ);
- //第二个参数表明Selector监听Channel时对什么事件感兴趣
- //SelectionKey.OP_CONNECT SelectionKey.OP_ACCEPT SelectionKey.OP_READ SelectionKey.OP_WRITE
- //可以用或操作符将多个兴趣组合一起
- SelectionKey
- 包含了interest集合 、ready集合 、Channel 、Selector 、附加的对象(可选)
- int interestSet = key.interestOps();可以进行类似interestSet & SelectionKey.OP_CONNECT的判断
- channel.configureBlocking(false);
- 使用:
- select():阻塞到至少有一个通道在你注册的事件上就绪了
- selectNow():不会阻塞,不管什么通道就绪都立刻返回
- selectedKeys():访问“已选择键集(selected key set)”中的就绪通道
- close():使用完selector需要用其close()方法会关闭该Selector,且使注册到该Selector上的所有SelectionKey实例无效
- Set selectedKeys = selector.selectedKeys();
- Iterator keyIterator = selectedKeys.iterator();
- while(keyIterator.hasNext()) {
- SelectionKey key = keyIterator.next();
- if(key.isAcceptable()) {
- // a connection was accepted by a ServerSocketChannel.
- } else if (key.isConnectable()) {
- // a connection was established with a remote server.
- } else if (key.isReadable()) {
- // a channel is ready for reading
- } else if (key.isWritable()) {
- // a channel is ready for writing
- }
- keyIterator.remove();//注意这里必须手动remove;表明该selectkey我已经处理过了;
- }
Java测试关键代码
- RandomAccessFile aFile = new RandomAccessFile("data/nio-data.txt", "rw");
- FileChannel inChannel = aFile.getChannel(); //从一个InputStream outputstream中获取channel
- //create buffer with capacity of 48 bytes
- ByteBuffer buf = ByteBuffer.allocate(48);
- int bytesRead = inChannel.read(buf); //read into buffer.
- while (bytesRead != -1) {
- buf.flip(); //make buffer ready for read
- while(buf.hasRemaining()){
- System.out.print((char) buf.get()); // read 1 byte at a time
- }
- buf.clear(); //make buffer ready for writing
- bytesRead = inChannel.read(buf);
- }
- aFile.close();
文件通道
- RandomAccessFile aFile = new RandomAccessFile("data/nio-data.txt", "rw");
- FileChannel inChannel = aFile.getChannel();
- ByteBuffer buf = ByteBuffer.allocate(48);
- int bytesRead = inChannel.read(buf);
- String newData = "New String to write to file..." + System.currentTimeMillis();
- ByteBuffer buf = ByteBuffer.allocate(48);
- buf.clear();
- buf.put(newData.getBytes());
- buf.flip();
- while(buf.hasRemaining()) {
- channel.write(buf);
- }
Socket 通道
- SocketChannel socketChannel = SocketChannel.open();
- socketChannel.connect(new InetSocketAddress("http://jenkov.com", 80));
- ByteBuffer buf = ByteBuffer.allocate(48);
- int bytesRead = socketChannel.read(buf);
- String newData = "New String to write to file..." + System.currentTimeMillis();
- ByteBuffer buf = ByteBuffer.allocate(48);
- buf.clear();
- buf.put(newData.getBytes());
- buf.flip();
- while(buf.hasRemaining()) {
- socketChannel.write(buf);
- }
ServerSocket 通道
- ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
- serverSocketChannel.socket().bind(new InetSocketAddress(9999));
- while(true){
- SocketChannel socketChannel =
- serverSocketChannel.accept();
- //do something with socketChannel...
- }
Datagram 通道(channel的读写操作与前面的有差异)
- DatagramChannel channel = DatagramChannel.open();
- channel.socket().bind(new InetSocketAddress(9999));
- ByteBuffer buf = ByteBuffer.allocate(48);
- buf.clear();
- channel.receive(buf);
- //receive()方法会将接收到的数据包内容复制到指定的Buffer. 如果Buffer容不下收到的数据,多出的数据将被丢弃。
- String newData = "New String to write to file..." + System.currentTimeMillis();
- ByteBuffer buf = ByteBuffer.allocate(48);
- buf.clear();
- buf.put(newData.getBytes());
- buf.flip();
- int bytesSent = channel.send(buf, new InetSocketAddress("jenkov.com", 80));
Java NIO 与 IO之间的区别的更多相关文章
- Java NIO 和 IO 的区别详解
Java NIO为jdk1.4提供了新的API,本文主要来比较一下Java中NIO和IO的区别,Java初学者可以了解一下. 下表总结了Java NIO和IO之间的主要差别,我会更详细地描述表中每部分 ...
- Java NIO和IO的区别(转)
原文链接:Java NIO和IO的区别 下表总结了Java NIO和IO之间的主要差别,我会更详细地描述表中每部分的差异. 复制代码代码如下: IO NIO面向流 ...
- java nio 与io区别
转自:http://blog.csdn.net/keda8997110/article/details/19549493 当学习了Java NIO和IO的API后,一个问题马上涌入脑海: 我应该何时使 ...
- Java NIO和IO的区别
下表总结了Java NIO和IO之间的主要差别,我会更详细地描述表中每部分的差异. 复制代码 代码如下: IO NIO面向流 面向缓冲阻塞IO 非阻塞IO无 选择器 面向流与面向缓冲 Java NIO ...
- Java NIO和IO的主要区别
From :http://blog.csdn.net/keda8997110/article/details/19549493 下表总结了Java NIO和IO之间的主要差别,我会更详细地描述表中每部 ...
- Java NIO与IO
当学习了Java NIO和IO的API后,一个问题立即涌入脑海: 我应该何时使用IO,何时使用NIO呢?在本文中,我会尽量清晰地解析Java NIO和IO的差异.它们的使用场景,以及它们怎样影响您的代 ...
- Java NIO 与 IO
我应该何时使用 IO,何时使用 NIO 呢?在本文中,我会尽量清晰地解析 Java NIO 和 IO 的差异.它们的使用场景,以及它们如何影响您的代码设计. Java NIO 和 IO 的主要区别 下 ...
- [转载] Java NIO与IO
原文地址:http://tutorials.jenkov.com/java-nio/nio-vs-io.html 作者:Jakob Jenkov 译者:郭蕾 校对:方腾飞 当学习了Java ...
- Java NIO系列教程(十二) Java NIO与IO
当学习了Java NIO和IO的API后,一个问题马上涌入脑海: 我应该何时使用IO,何时使用NIO呢?在本文中,我会尽量清晰地解析Java NIO和IO的差异.它们的使用场景,以及它们如何影响您的代 ...
随机推荐
- 【Visual Studio】VS发布应用未能创建默认证书的问题解决方法
解决方法:点击你创建的项目 右键> 属性>签名>从存储区选择>选择证书这时候显示无可用证书 ,然后我从文件区选择了一个结果,又出现了第二个问题.提示我“签名时出错: 指定了无效 ...
- Effective Java 第三版——50. 必要时进行防御性拷贝
Tips 书中的源代码地址:https://github.com/jbloch/effective-java-3e-source-code 注意,书中的有些代码里方法是基于Java 9 API中的,所 ...
- [svc]linux下网桥-docker网桥
网桥和交换机 2口交换机=网桥 交换机: 工作在数据链路层,根据源mac学习(控制层),目的mac转发(数据层). linux的网卡 vmware workstation中的桥接 参考: http:/ ...
- 《转》Babel 入门教程
ECMAScript 6是JavaScript语言的下一代标准,已经在2015年6月正式发布了.Mozilla公司将在这个标准的基础上,推出JavaScript 2.0.ES6的目标,是使得JavaS ...
- vue环境配置脚手架环境搭建vue工程目录
首先在初始化一个vue项目之前,我们需要下载node.js,并且安装! 下载地址: nodejs.cn/download 安装完成之后,windows+r 运行命令 cmd 输入node -v 检 ...
- idea git 使用
第一部 测试 本地git 是否已经成功安装 centos 7 发行版默认已经安装 第二部: 测试 github 连接是否成功,需要输入用户密码 第三部:创建git项目管理两种方式 1. 菜单 VC ...
- 【iCore4 双核心板_ARM】例程六:IWDG看门狗实验——复位ARM
实验原理: STM32内部包含独立看门狗,通过看门狗可以监控程序远行,程序运行错误时, 未在规定时间内喂狗,自动复位ARM.本实验通过按键按下,停止喂狗,制造程序运行 错误,从而产生复位. 核心代码: ...
- java-信息安全(十)-数字签名算法DSA
概述 信息安全基本概念: DSA算法(Digital Signature Algorithm,数据签名算法) DSA Digital Signature Algorithm (DSA)是Schnorr ...
- Scala学习笔记(五):内建控制循环
前言 Scala中内建控制循环包括if.while.for.try.match和函数调用. if和while与java类似,不做介绍. for 基础用法 def main(args: Array[St ...
- for循环 while循环 break跳出循环 continue结束本次循环 exit退出整个脚本