下面关于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的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。