下面关于java nio提供了与标准io不同的io工作方式,IO和NIO

  下面关于java nio提供了与标准io不同的io工作方式,IO和NIO

  在传统的socket IO中,需要为每个连接创建一个线程。当并发连接数巨大时,线程占用的堆栈内存和CPU线程切换的开销都会巨大。有了NIO,不再需要为每个线程创建单独的线程。您可以使用具有有限数量线程的线程池,甚至可以使用一个线程来服务任意数量的连接。因为线程数小于连接数,所以每个线程都不能阻塞IO操作。如果它阻塞,一些连接将不会被处理。NIO提供了这种非阻塞能力。

  少量的线程如何同时服务于大量的连接?答案是现成的选择。就像去饭店吃一顿饭。每次有客人来桌,都会有服务员为你服务。从到餐厅到结账,这种方式的好处是服务质量好,一对一服务是VIP。但是缺点也很明显,成本高。如果餐厅做得好,同时有100个客人,就需要100个服务员,老板发工资的时候会心痛。这是连接线的传统方式。

  谁是老板?他很熟练。这让老板很纳闷,10个服务员怎么同时服务100个客人。老板发现服务员在服务客人的过程中并不总是很忙。在客人点餐、上菜、吃饭的时间里,服务员都是闲着的,但是这个服务员还是被这一桌的客人占着,不能服务其他客人。用华为领导的话说,他的工作并不充实。那如何利用这种空闲时间呢?餐馆老板想了一个办法,让一个服务员(前台)负责收集客人的需求并登记。比如客人进来,客人点餐,客人要结账,都是先记录下来,按顺序排好。每个服务员来这里都是得到一个需求,比如点菜,然后拿着菜单帮客人点菜。点好菜后,服务员马上回来,接到下一个需求,继续服务其他客人。这种方式的服务质量还不如一对一的服务,客人数据多的时候可能要等。但是好处也很明显。因为在客人吃饭的时候服务员不必闲着,服务员可以在这段时间服务其他客人。原来10个服务员最多同时服务10桌,现在可能服务50桌,60个客人。

  这种服务模式与传统服务模式有两个不同之处:

  1.添加了一个角色。应该有一个人负责收集客人的需求。NIO中对应的是选择器。

  2.从阻断服务模式到非阻断服务模式,服务员不用在客人吃饭的时候一直守在客人身边。传统的IO操作,比如read(),当没有数据读取时,线程被阻塞占用,直到数据到达。当NIO中没有要读取的数据时,read()会立即返回0,线程不会阻塞。

  在NIO中,客户端创建连接后,必须先向Selector注册连接,相当于在客人进入餐厅后告诉前台你要用餐。前台会告诉你你的桌号,然后你可以去那张桌子坐下。SelectionKey是表格编号。当某桌需要服务时,前台会记录哪桌需要什么服务,比如1桌需要点餐,2桌需要结账。服务员从前台拿一份记录,根据记录提供服务,然后回来下一个。这样,服务时间得到了最有效的利用。

  简介:

  J2SE1.4及以上版本发布了全新的I/O类库。NIO库提供的一些新特性:非阻塞I/O、字符转换、缓冲和通道。而NIO和IO都在rt.jar包里。

  一. NIO简介

  NIO包(java.nio.*)引入了四种关键的抽象数据类型,共同解决了传统I/O类中的一些问题。

  1.Buffer:是一种线性表结构,包含数据,用于读写。它还为内存映射文件的I/O操作提供了一个特殊的类。

  2.Charset:它提供了将Unicode字符串映射到字节序列和反向映射的操作。

  3.通道:它包含套接字、文件和管道,实际上是一个双向通信通道。

  4.选择器:它将多个异步I/O操作集中到一个或多个线程中(可以看作Unix中select()函数或Win32中WaitForSingleEvent()函数的面向对象版本)。

  二、传统的io方式

  以网络应用为例。在传统方式中,需要监控一个ServerSocket,接受请求的连接为其提供服务(服务通常包括处理请求和发送响应)。图1是服务器的生命周期图,其中用粗黑线标记的部分表示将发生I/O阻塞。

  图1

  您可以分析创建服务器的每个具体步骤。首先,创建服务器套接字

  server socket server=new server socket(10000);

  然后接受新的连接请求。

  socket newCnotallow=server . accept();

  对accept方法的调用将一直阻塞,直到ServerSocket收到连接请求。一旦连接请求被接受,服务器就可以在客户机套接字中读取请求。

  1 InputStream in=new connection . getinputstream();2

  3 InputStreamReader reader=new InputStreamReader(in);四

  5 buffered reader buffer=new buffered reader(reader);六

  7请求Request=new Request();八

  9 while(!request.isComplete()) { 10

  11 String line=buffer . readline();12

  13 request . addline(line);14}这个操作有两个问题。第一,BufferedReader类的readLine()方法在其缓冲区未满时会导致线程阻塞,只有当某些数据填满缓冲区或者客户端关闭套接字时,该方法才会返回。其次,它产生大量的垃圾。BufferedReader创建一个缓冲区来从客户端套接字读取数据,但也创建一些字符串来存储数据。虽然BufferedReader内部提供了StringBuffer来处理这个问题,但是所有的字符串很快就变成了垃圾,需要回收。

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

  1响应Response=request . generate Response();2

  3 output stream out=new connection . get output stream();四

  5 InputStream in=response . getinputstream();六

  7 int ch八

  9 while(-1!=(ch=in . read()){ 10

  11 out . write(ch);12

  13 } 14

  15 new connection . close();同样,读写操作被阻塞,一次向流中写入一个字符会导致效率低下,所以应该使用缓冲区,但是一旦使用缓冲区,流就会产生更多的垃圾。

  传统解决方案

  通常,在Java中需要线程(大量线程)来处理阻塞的I/O。通常,实现一个线程池来处理请求,如图2所示。

  图二

  线程化服务器可以处理多个连接,但是它们也会导致许多问题。每个线程都有自己的堆栈空间,占用一些CPU时间,开销很大,很多时间浪费在阻塞的I/O操作上,CPU没有得到有效利用。

  三。NIO的详细介绍

  1.****缓冲器

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

  java.nio.Buffer的类描述

  Java.nio.ByteBuffer包含字节类型。可以从ReadableByteChannel中读取,在WritableByteChannel中写入。

  Java.nio.MappedByteBuffer包含字节类型,直接映射到内存的某个区域。

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

  Java.nio.DoubleBuffer包含double类型,无法写入通道。

  Java.nio.FloatBuffer包含浮点类型。

  Java.nio.IntBuffer包含int类型。

  Java.nio.LongBuffer包含一个长类型。

  Java.nio.ShortBuffer包含短类型。

  可以通过调用allocate(int capacity)方法或allocateDirect(int capacity)方法来分配缓冲区。具体来说,可以通过调用filechannel.map (int mode,long position,int size)来创建MappedBytesBuffer。直接)缓冲区在内存中分配一个连续的块,并使用本地访问方法读写数据。非直接缓冲区通过使用Java中的数组访问代码来读写数据。有时候需要使用间接缓冲,比如使用任何wrap方法(比如ByteBuffer.wrap(byte[]))来创建基于Java数组的缓冲区。

  2.字符编码

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

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

  3.频道* ****(频道)

  您可能会注意到,现有的java.io类都不能读写缓冲区类型,因此NIO中提供了Channel类来读写缓冲区。信道可以被认为是连接,它可以是到特定设备、程序或网络的连接。通道的类层次结构图如下

  图3

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

  GatheringByteChannel可以一次将数据从多个缓冲区写入通道,而ScatteringByteChannel可以一次将数据从通道读取到多个缓冲区。您还可以设置通道来服务阻塞或非阻塞I/O操作。

  为了使通道与传统的I/O类兼容,通道类提供了一个静态方法来创建流或读取器。

  4.****选择器

  在过去的阻塞I/O中,我们通常知道何时可以读取或写入流,因为方法调用会返回,直到流准备好。但是对于非阻塞通道,我们需要某种方法来知道通道何时准备好。在NIO包中,选择器就是为此而设计的。SelectableChannel可以注册一个特定的事件,而不是在事件发生时通知应用程序,通道跟踪该事件。然后,当应用程序调用选择器上的任何选择方法时,它会检查注册的通道,以查看是否有任何感兴趣的事件。图4显示了选择器和两个注册通道的示例。

  图4

  并非所有渠道都支持所有操作。SelectionKey类定义了所有可能的操作位,将使用两次。首先,当应用程序调用可选通道时。注册(选择器Sel,Intop)方法来注册通道,它将所需的操作作为第二个参数传递给该方法。然后,一旦选择了SelectionKey,SelectionKey的readyOps()方法将返回所有通道支持的操作的数字总和。SelectableChannel的validOps方法返回每个通道允许的操作。不支持的注册通道操作将引发IllegalArgumentException异常。下表列出了SelectableChannel子类支持的操作。

  1服务器套接字通道OP_ACCEPT 2

  3套接字通道OP_CONNECT、OP_READ、OP_WRITE 4

  5数据通道OP_READ,OP_WRITE 6

  7烟斗。源通道OP_READ 8

  9烟斗。SinkChannel OP_WRITE IV。举例说明

  1.简单的网页内容下载

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

  包examples.nio

  包com . yineng . mycat;导入Java . nio . byte buffer;导入Java . nio . channels . socket channel;导入Java . nio . charset . charset;导入Java . net . inetsocketaddress;导入Java . io . io exception;/* * * * @作者kzfy

  * @data 2015/12/21 */

  公共类socket channel reader { private Charset Charset=Charset . forname( UTF-8 );//创建UTF 8字符集

  专用SocketChannel信道;public void getHTMLContent(){ try {

  connect();

  send request();

  read response();

  }catch(IOException e){

  system . err . println(e . tostring());

  }最后{如果(频道!=null){ try{

  channel . close();

  }catch(IOException e){}

  }

  }

  } privatevidconnect()ThrowsioException {//连接到CSDN inetSocket地址Socket Address=NewinetSocket地址( 3358 www.csdn.net ,80);

  channel=socket channel . open(socket address);//使用工厂方法open创建一个通道,并将其连接到指定的地址。//这相当于socketchannel.open()。connect(socket address);invoke } private void send request()throwsioexception {

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

  } private void Read response()throwsioexception {//读取响应

  byte buffer buffer=byte buffer . allocate(1024);//创建一个1024字节的缓冲区

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

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

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

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

  }

  } public static void main(String[]args){ new socket channel reader()。getHTMLContent();

  }

  }版权归作者所有:来自博客作者为温度原创作品。转载请联系作者获得授权,否则将追究法律责任。

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: