java.io和util的区别_Java NIO与IO的区别和比较

Java NIO与IO的区别和比较

导读

J2SE1.4以上版本中发布了全新的I/O类库。本文将通过一些实例来简单介绍NIO库提供的一些新特性:非阻塞I/O,字符转换,缓冲以及通道。

一. 介绍NIO

NIO包(java.nio.*)引入了四个关键的抽象数据类型,它们共同解决传统的I/O类中的一些问题。Buffer:它是包含数据且用于读写的线形表结构。其中还提供了一个特殊类用于内存映射文件的I/O操作。

Charset:它提供Unicode字符串影射到字节序列以及逆影射的操作。

Channels:包含socket,file和pipe三种管道,它实际上是双向交流的通道。

Selector:它将多元异步I/O操作集中到一个或多个线程中(它可以被看成是Unix中select()函数或Win32中WaitForSingleEvent()函数的面向对象版本)。

二. 回顾传统

在介绍NIO之前,有必要了解传统的I/O操作的方式。以网络应用为例,传统方式需要监听一个ServerSocket,接受请求的连接为其提供服务(服务通常包括了处理请求并发送响应)图一是服务器的生命周期图,其中标有粗黑线条的部分表明会发生I/O阻塞。

92118d80e076a39b973f7b62490c1baa.png

图一

//可以分析创建服务器的每个具体步骤。首先创建ServerSocket

ServerSocket server=new ServerSocket(10000);//然后接受新的连接请求

Socket newConnection=server.accept();//对于accept方法的调用将造成阻塞,直到ServerSocket接受到一个连接请求为止。一旦连接请求被接受,服务器可以读客户socket中的请求。

InputStream in =newConnection.getInputStream();

InputStreamReader reader= newInputStreamReader(in);

BufferedReader buffer= newBufferedReader(reader);

Request request= newRequest();while(!request.isComplete()) {

String line=buffer.readLine();

request.addLine(line);

这样的操作有两个问题,首先BufferedReader类的readLine()方法在其缓冲区未满时会造成线程阻塞,只有一定数据填满了缓冲区或者客户关闭了套接字,方法才会返回。其次,它回产生大量的垃圾,BufferedReader创建了缓冲区来从客户套接字读入数据,但是同样创建了一些字符串存储这些数据。虽然BufferedReader内部提供了StringBuffer处理这一问题,但是所有的String很快变成了垃圾需要回收。

同样的问题在发送响应代码中也存在

Response response =request.generateResponse();

OutputStream out=newConnection.getOutputStream();

InputStream in=response.getInputStream();intch;while(-1 != (ch =in.read())) {

out.write(ch);

}

newConnection.close();

类似的,读写操作被阻塞而且向流中一次写入一个字符会造成效率低下,所以应该使用缓冲区,但是一旦使用缓冲,流又会产生更多的垃圾。

传统的解决方法

通常在Java中处理阻塞I/O要用到线程(大量的线程)。一般是实现一个线程池用来处理请求,如图二

34ba5b548f7fc838581d39b7f38059d7.png

图二

线程使得服务器可以处理多个连接,但是它们也同样引发了许多问题。每个线程拥有自己的栈空间并且占用一些CPU时间,耗费很大,而且很多时间是浪费在阻塞的I/O操作上,没有有效的利用CPU。

三. 新I/O

1. Buffer

传统的I/O不断的浪费对象资源(通常是String)。新I/O通过使用Buffer读写数据避免了资源浪费。Buffer对象是线性的,有序的数据集合,它根据其类别只包含唯一的数据类型。

java.nio.Buffer //类描述

java.nio.ByteBuffer //包含字节类型。 可以从ReadableByteChannel中读在 WritableByteChannel中写

java.nio.MappedByteBuffer //包含字节类型,直接在内存某一区域映射

java.nio.CharBuffer //包含字符类型,不能写入通道

java.nio.DoubleBuffer //包含double类型,不能写入通道

java.nio.FloatBuffer //包含float类型

java.nio.IntBuffer //包含int类型

java.nio.LongBuffer //包含long类型

java.nio.ShortBuffer //包含short类型

可以通过调用allocate(int capacity)方法或者allocateDirect(int capacity)方法分配一个Buffer。特别的,你可以创建MappedBytesBuffer通过调用FileChannel.map(int mode,long position,int size)。直接(direct)buffer在内存中分配一段连续的块并使用本地访问方法读写数据。非直接(nondirect)buffer通过使用Java中的数组访问代码读写数据。有时候必须使用非直接缓冲例如使用任何的wrap方法(如ByteBuffer.wrap(byte[]))在Java数组基础上创建buffer。

2. 字符编码

向ByteBuffer中存放数据涉及到两个问题:字节的顺序和字符转换。ByteBuffer内部通过ByteOrder类处理了字节顺序问题,但是并没有处理字符转换。事实上,ByteBuffer没有提供方法读写String。

Java.nio.charset.Charset处理了字符转换问题。它通过构造CharsetEncoder和CharsetDecoder将字符序列转换成字节和逆转换。

3. 通道(Channel)

你可能注意到现有的java.io类中没有一个能够读写Buffer类型,所以NIO中提供了Channel类来读写Buffer。通道可以认为是一种连接,可以是到特定设备,程序或者是网络的连接。通道的类等级结构图如下

06f4f2c94bf9797d9c3b86d4e118e086.png

图三

图中ReadableByteChannel和WritableByteChannel分别用于读写。

GatheringByteChannel可以从使用一次将多个Buffer中的数据写入通道,相反的,ScatteringByteChannel则可以一次将数据从通道读入多个Buffer中。你还可以设置通道使其为阻塞或非阻塞I/O操作服务。

为了使通道能够同传统I/O类相容,Channel类提供了静态方法创建Stream或Reader

4. Selector

在过去的阻塞I/O中,我们一般知道什么时候可以向stream中读或写,因为方法调用直到stream准备好时返回。但是使用非阻塞通道,我们需要一些方法来知道什么时候通道准备好了。在NIO包中,设计Selector就是为了这个目的。SelectableChannel可以注册特定的事件,而不是在事件发生时通知应用,通道跟踪事件。然后,当应用调用Selector上的任意一个selection方法时,它查看注册了的通道看是否有任何感兴趣的事件发生。图四是selector和两个已注册的通道的例子

73baa534e24b22bc89bfdbd5dd152696.png

并不是所有的通道都支持所有的操作。SelectionKey类定义了所有可能的操作位,将要用两次。首先,当应用调用SelectableChannel.register(Selector sel,int op)方法注册通道时,它将所需操作作为第二个参数传递到方法中。然后,一旦SelectionKey被选中了,SelectionKey的readyOps()方法返回所有通道支持操作的数位的和。SelectableChannel的validOps方法返回每个通道允许的操作。注册通道不支持的操作将引发IllegalArgumentException异常。下表列出了SelectableChannel子类所支持的操作。

IllegalArgumentException异常。下表列出了SelectableChannel子类所支持的操作。

ServerSocketChannel OP_ACCEPT

SocketChannel OP_CONNECT, OP_READ, OP_WRITE

DatagramChannel OP_READ, OP_WRITE

Pipe.SourceChannel OP_READ

Pipe.SinkChannel OP_WRITE

四. 举例说明

1. 简单网页内容下载

这个例子非常简单,类SocketChannelReader使用SocketChannel来下载特定网页的HTML内容。

importjava.nio.ByteBuffer;importjava.nio.channels.SocketChannel;importjava.nio.charset.Charset;importjava.net.InetSocketAddress;importjava.io.IOException;public classSocketChannelReader {private Charset charset = Charset.forName("UTF-8");//创建UTF-8字符集

privateSocketChannel channel;public voidgetHTMLContent() {try{

connect();

sendRequest();

readResponse();

}catch(IOException e) {

System.err.println(e.toString());

}finally{if (channel != null) {try{

channel.close();

}catch(IOException e) {

}

}

}

}private void connect() throws IOException {//连接到CSDN

InetSocketAddress socketAddress = newInetSocketAddress("www.csdn.net", 80);

channel=SocketChannel.open(socketAddress);//使用工厂方法open创建一个channel并将它连接到指定地址上//相当与SocketChannel.open().connect(socketAddress);调用

}private void sendRequest() throwsIOException {

channel.write(charset.encode("GET " + "/document" + "\r\n\r\n"));//发送GET请求到CSDN的文档中心//使用channel.write方法,它需要CharByte类型的参数,使用//Charset.encode(String)方法转换字符串。

}private void readResponse() throws IOException {//读取应答

ByteBuffer buffer = ByteBuffer.allocate(1024);//创建1024字节的缓冲

while (channel.read(buffer) != -1) {

buffer.flip();//flip方法在读缓冲区字节操作之前调用。

System.out.println(charset.decode(buffer));//使用Charset.decode方法将字节转换为字符串

buffer.clear();//清空缓冲

}

}public static voidmain(String[] args) {try{newSocketChannelReader().getHTMLContent();

}catch(Exception e) {

e.printStackTrace();

}

}

}

2. 简单的加法服务器和客户机

服务器代码

packagemjorcen.nio.test;importjava.nio.ByteBuffer;importjava.nio.IntBuffer;importjava.nio.channels.ServerSocketChannel;importjava.nio.channels.SocketChannel;importjava.net.InetSocketAddress;importjava.io.IOException;/*** SumServer.java

*

*

* Created: Thu Nov 06 11:41:52 2003

*

*@authorstarchu1981

*@version1.0*/

public classSumServer {private ByteBuffer _buffer = ByteBuffer.allocate(8);private IntBuffer _intBuffer =_buffer.asIntBuffer();private SocketChannel _clientChannel = null;private ServerSocketChannel _serverChannel = null;public voidstart() {try{

openChannel();

waitForConnection();

}catch(IOException e) {

System.err.println(e.toString());

}

}private void openChannel() throwsIOException {

_serverChannel=ServerSocketChannel.open();

_serverChannel.socket().bind(new InetSocketAddress(10000));

System.out.println("服务器通道已经打开");

}private void waitForConnection() throwsIOException {while (true) {

_clientChannel=_serverChannel.accept();if (_clientChannel != null) {

System.out.println("新的连接加入");

processRequest();

_clientChannel.close();

}

}

}private void processRequest() throwsIOException {

_buffer.clear();

_clientChannel.read(_buffer);int result = _intBuffer.get(0) + _intBuffer.get(1);

_buffer.flip();

_buffer.clear();

_intBuffer.put(0, result);

_clientChannel.write(_buffer);

}public static voidmain(String[] args) {newSumServer().start();

}

}//SumServer

客户代码

packagemjorcen.nio.test;importjava.nio.ByteBuffer;importjava.nio.IntBuffer;importjava.nio.channels.SocketChannel;importjava.net.InetSocketAddress;importjava.io.IOException;/*** SumClient.java

*

*

* Created: Thu Nov 06 11:26:06 2003

*

*@authorstarchu1981

*@version1.0*/

public classSumClient {private ByteBuffer _buffer = ByteBuffer.allocate(8);privateIntBuffer _intBuffer;privateSocketChannel _channel;publicSumClient() {

_intBuffer=_buffer.asIntBuffer();

}//SumClient constructor

public int getSum(int first, intsecond) {int result = 0;try{

_channel=connect();

sendSumRequest(first, second);

result=receiveResponse();

}catch(IOException e) {

System.err.println(e.toString());

}finally{if (_channel != null) {try{

_channel.close();

}catch(IOException e) {

}

}

}returnresult;

}private SocketChannel connect() throwsIOException {

InetSocketAddress socketAddress= new InetSocketAddress("localhost",10000);returnSocketChannel.open(socketAddress);

}private void sendSumRequest(int first, int second) throwsIOException {

_buffer.clear();

_intBuffer.put(0, first);

_intBuffer.put(1, second);

_channel.write(_buffer);

System.out.println("发送加法请求 " + first + "+" +second);

}private int receiveResponse() throwsIOException {

_buffer.clear();

_channel.read(_buffer);return _intBuffer.get(0);

}public static voidmain(String[] args) {

SumClient sumClient= newSumClient();

System.out.println("加法结果为 :" + sumClient.getSum(100, 324));

}

}//SumClient

3. 非阻塞的加法服务器

首先在openChannel方法中加入语句

_serverChannel.configureBlocking(false);//设置成为非阻塞模式

重写WaitForConnection方法的代码如下,使用非阻塞方式 (这里我稍微改了下,不知道对不对, 原来博主的跑不了,)

private void waitForConnection() throwsIOException {

Selector acceptSelector=SelectorProvider.provider().openSelector();/** 在服务器套接字上注册selector并设置为接受accept方法的通知。

* 这就告诉Selector,套接字想要在accept操作发生时被放在ready表 上,因此,允许多元非阻塞I/O发生。*/SelectionKey acceptKey=_serverChannel.register(acceptSelector,

SelectionKey.OP_ACCEPT);int keysAdded = 0;/*select方法在任何上面注册了的操作发生时返回*/

while ((keysAdded = acceptSelector.select()) > 0) {//某客户已经准备好可以进行I/O操作了,获取其ready键集合

Set readyKeys =acceptSelector.selectedKeys();

Iterator i=readyKeys.iterator();//遍历ready键集合,并处理加法请求

while(i.hasNext()) {

SelectionKey sk=(SelectionKey) i.next();

i.remove();

ServerSocketChannel nextReady=(ServerSocketChannel) sk

.channel();//接受加法请求并处理它

_clientChannel =nextReady.accept().socket().getChannel();

processRequest();

_clientChannel.close();

}

}

}

服务端整体改完之后代码:

packagemjorcen.nio.test;importjava.io.IOException;importjava.net.InetSocketAddress;importjava.net.Socket;importjava.nio.ByteBuffer;importjava.nio.IntBuffer;importjava.nio.channels.SelectableChannel;importjava.nio.channels.SelectionKey;importjava.nio.channels.Selector;importjava.nio.channels.ServerSocketChannel;importjava.nio.channels.SocketChannel;importjava.nio.channels.spi.SelectorProvider;importjava.util.Iterator;importjava.util.Set;/*** SumServer.java

*

*

* Created: Thu Nov 06 11:41:52 2003

*

*@authorstarchu1981

*@version1.0*/

public classSumServer {private ByteBuffer _buffer = ByteBuffer.allocate(8);private IntBuffer _intBuffer =_buffer.asIntBuffer();private SocketChannel _clientChannel = null;private ServerSocketChannel _serverChannel = null;public voidstart() {try{

openChannel();

waitForConnection();

}catch(IOException e) {

System.err.println(e.toString());

}

}private void openChannel() throwsIOException {

_serverChannel=ServerSocketChannel.open();

_serverChannel.socket().bind(new InetSocketAddress(10000));

_serverChannel.configureBlocking(false);//设置成为非阻塞模式

System.out.println("服务器通道已经打开");

}private void waitForConnection() throwsIOException {

Selector acceptSelector=SelectorProvider.provider().openSelector();/** 在服务器套接字上注册selector并设置为接受accept方法的通知。

* 这就告诉Selector,套接字想要在accept操作发生时被放在ready表 上,因此,允许多元非阻塞I/O发生。*/SelectionKey acceptKey=_serverChannel.register(acceptSelector,

SelectionKey.OP_ACCEPT);int keysAdded = 0;/*select方法在任何上面注册了的操作发生时返回*/

while ((keysAdded = acceptSelector.select()) > 0) {//某客户已经准备好可以进行I/O操作了,获取其ready键集合

Set readyKeys =acceptSelector.selectedKeys();

Iterator i=readyKeys.iterator();//遍历ready键集合,并处理加法请求

while(i.hasNext()) {

SelectionKey sk=(SelectionKey) i.next();

i.remove();

ServerSocketChannel nextReady=(ServerSocketChannel) sk

.channel();//接受加法请求并处理它

_clientChannel =nextReady.accept().socket().getChannel();

processRequest();

_clientChannel.close();

}

}

}private void processRequest() throwsIOException {

_buffer.clear();

_clientChannel.read(_buffer);int result = _intBuffer.get(0) + _intBuffer.get(1);

_buffer.flip();

_buffer.clear();

_intBuffer.put(0, result);

_clientChannel.write(_buffer);

}public static voidmain(String[] args) {newSumServer().start();

}

}//SumServer

来自 :  http://blog.chinaunix.net/uid-24186189-id-2623973.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/541925.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java LocalDate类| isSupported()方法与示例

LocalDate类isSupported()方法 (LocalDate Class isSupported() method) Syntax: 句法: public boolean isSupported (TemporalField t_field);public boolean isSupported (TemporalUnit t_unit);isSupported() method is available in java.time package. isSupp…

区块链+税务的思考

2016年,区块链技术火了!各大金融公司、互联网巨头都竞相参加到区块链技术的研究中。我们公司的业务是税务的信息化领域,也希望通过区块链技术的应用,来提升为财税领域的服务。 区块链技术优缺点总结 下图是对区块链技术的一些特点…

java hasset 顺序_java集合排序问题

List: 元素是有序的,元素可以重复,因为该集合体系有索引(脚标)常用的子类对象:1————ArrayList 底层的数据结构是使用的数组结构特点:查询速度快,但是增删比较慢2————LinkedList底层的数据结构使用的是链表结构…

如何使用JavaScript删除CSS属性?

In this article, well see how we can remove a CSS property from a certain element using JavaScript? We can remove only those properties that we assign ourselves and the pre-default ones cannot be removed by this method. 在本文中,我们将看到如何使…

Django 缓存系统

Django 是动态网站,一般来说需要实时地生成访问的网页,展示给访问者,这样,内容可以随时变化,但是从数据库读多次把所需要的数据取出来,要比从内存或者硬盘等一次读出来 付出的成本大很多。 缓存系统工作原理…

java web截屏_java_WebDriver中实现对特定的Web区域截图方法,用过 WebDriver 的同学都知道,We - phpStudy...

WebDriver中实现对特定的Web区域截图方法用过 WebDriver 的同学都知道,WebDriver 可以对浏览器中的页面进行截图。例如:public byte[] takeScreenshot() throws IOException {TakesScreenshot takesScreenshot (TakesScreenshot) driver;return takesSc…

c语言 关键字const_C ++ const关键字| 查找输出程序| 套装1

c语言 关键字constProgram 1: 程序1&#xff1a; #include <iostream>using namespace std;void fun(int& A) const{A 10;}int main(){int X 0;fun(X);cout << X;return 0;}Output: 输出&#xff1a; [Error] non-member function void fun(int) cannot ha…

【喜报】JEEWX荣获“2016 年度码云新增热门开源软件排行榜”第一名!

为什么80%的码农都做不了架构师&#xff1f;>>> 2016 年度码云新增项目排行榜 TOP 50 正式出炉&#xff01;根据 2016 年在码云上新增开源项目的 Watch、Star、Fork 数量以及其他角度的统计&#xff0c;JEEWX捷微管家荣获“2016 年度码云新增热门开源软件排行榜”第…

java 二叉树特点_疯狂java笔记之树和二叉树

树的概述树是一种非常常用的数据结构&#xff0c;树与前面介绍的线性表&#xff0c;栈&#xff0c;队列等线性结构不同&#xff0c;树是一种非线性结构1.树的定义和基本术语计算机世界里的树&#xff0c;是从自然界中实际的树抽象而来的&#xff0c;它指的是N个有父子关系的节点…

编辑距离 dp_使用动态编程(DP)编辑距离

编辑距离 dpProblem: You are given two strings s1 and s2 of length M and N respectively. You can perform following operations on the string. 问题&#xff1a;给您两个长度分别为M和N的字符串s1和s2 。 您可以对字符串执行以下操作。 Insert a character at any posi…

tomcat +apache 配置集群

2019独角兽企业重金招聘Python工程师标准>>> APACHE2.2.25TOMCAT6.0.37配置负载均衡 目标: 使用 apache 和 tomcat 配置一个可以应用的 web 网站&#xff0c;要达到以下要求&#xff1a; 1. Apache 做为 HttpServer &#xff0c;后面连接多个 tomcat 应用实例&…

java双缓存机制_详解JVM类加载机制及类缓存问题的处理方法

前言大家应该都知道&#xff0c;当一个Java项目启动的时候&#xff0c;JVM会找到main方法&#xff0c;根据对象之间的调用来对class文件和所引用的jar包中的class文件进行加载(其步骤分为加载、验证、准备、解析、初始化、使用和卸载)&#xff0c;方法区中开辟内存来存储类的运…

oracle中dbms_并发和由于DBMS中的并发导致的问题

oracle中dbms并发 (Concurrency) The ability of a database system which handles simultaneously or a number of transactions by interleaving parts of the actions or the overlapping this is called concurrency of the system. 数据库系统通过交织部分操作或重叠操作来…

什么是mvc?

什么是MVCMVC 是一种设计模式&#xff0c;它将应用划分为3 个部分&#xff1a;数据&#xff08;模型&#xff09;、展现层&#xff08;视图&#xff09;和用户交互层&#xff08;控制器&#xff09;。换句话说&#xff0c;一个事件的发生是这样的过程&#xff1a;1&#xff0e;…

mysql的安装和基本命令_MySQL安装以及简单命令用法

MYSQL:关系型数据库存储引擎:负责将逻辑层的概念转化为物理层机制&#xff0c;在物理层完成物理机制。支持事务&#xff1a;transaction必须满足的条件&#xff1a;ACID(一致性,持久性,原子性,隔离性)锁&#xff1a;并发访问随机访问&#xff1a;数据在磁盘上是随机存储的安装&…

将数组转换为JavaScript中的对象

Lets say you have the following array, 假设您有以下数组&#xff0c; const nums [1, 2, 3, 4, 5];console.log(nums);Output 输出量 (5) [1, 2, 3, 4, 5]We know that nums is an array and we can see in the output that we get an array. Lets convert it into an ob…

docker集群运行在calico网络上

2019独角兽企业重金招聘Python工程师标准>>> ##网络及版本信息 docker1 centos7 192.168.75.200 docker2 centos7 192.168.75.201 物理网络 192.168.75.1/24 Docker version 1.10.3, build 3999ccb-unsupported &#xff0c;安装过程略 # calicoctl version Version…

python批量雷达图_python批量制作雷达图

老板要画雷达图&#xff0c;但是数据好多组怎么办&#xff1f;不能一个一个点excel去画吧&#xff0c;那么可以利用python进行批量制作&#xff0c;得到样式如下&#xff1a;首先制作一个演示的excel&#xff0c;评分为excel随机数生成&#xff1a;1 INT((RAND()4)*10)/10加入标…

JavaScript中带有示例的Math.log()方法

JavaScript | Math.log()方法 (JavaScript | Math.log() Method) Math.log() is a function in math library of JavaScript that is used to return the value of natural Log i.e. (base e) of the given number. It is also known as ln(x) in mathematical terms. Math.log…

SUI踩坑记录

SUI踩坑记录 最近做了个项目选型了SUI和vue做单页应用。下面记录一下踩坑经历SUI 介绍 sui文档&#xff1a;http://m.sui.taobao.org/SUI Mobile 是一套基于 Framework7 开发的UI库。它非常轻量、精美&#xff0c;只需要引入我们的CDN文件就可以使用&#xff0c;并且能兼容到 i…