`
seara
  • 浏览: 624096 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

Java网络编程从入门到精通(33):非阻塞I/O的缓冲区(Buffer)

阅读更多
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <meta name="ProgId" content="Word.Document"> <meta name="Generator" content="Microsoft Word 11"> <meta name="Originator" content="Microsoft Word 11"> <link rel="File-List" href="file:///C:%5CDOCUME%7E1%5CADMINI%7E1%5CLOCALS%7E1%5CTemp%5Cmsohtml1%5C01%5Cclip_filelist.xml"> <!--[if gte mso 9]><xml> Normal 0 7.8 磅 0 2 false false false MicrosoftInternetExplorer4 </xml><![endif]--><!--[if gte mso 9]><![endif]--><!--[if !mso]> <style> st1":*{behavior:url(#ieooui) } </style> <![endif]--><style> <!-- /* Font Definitions */ &#64;font-face {font-family:Wingdings; panose-1:5 0 0 0 0 0 0 0 0 0;} &#64;font-face {font-family:宋体; panose-1:2 1 6 0 3 1 1 1 1 1;} &#64;font-face {font-family:""&#64;宋体"; panose-1:2 1 6 0 3 1 1 1 1 1;} /* Style Definitions */ p.MsoNormal, li.MsoNormal, div.MsoNormal {mso-style-parent:""; margin:0cm; margin-bottom:.0001pt; text-align:justify; text-justify:inter-ideograph; font-size:10.5pt; font-family:"Times New Roman";} /* Page Definitions */ &#64;page {} &#64;page Section1 {size:612.0pt 792.0pt; margin:72.0pt 90.0pt 72.0pt 90.0pt;} div.Section1 {page:Section1;} /* List Definitions */ &#64;list l0 {} &#64;list l0:level1 { margin-left:42.0pt; text-indent:-21.0pt; font-family:Wingdings;} ol {margin-bottom:0cm;} ul {margin-bottom:0cm;} --> </style> <!--[if gte mso 10]> <style> /* Style Definitions */ table.MsoNormalTable { mso-style-parent:""; font-size:10.0pt; font-family:"Times New Roman";} </style> <![endif]-->

本文为原创,如需转载,请注明作者和出处,谢谢!

上一篇:Java网络编程从入门到精通(32):一个非阻塞I/O的例子

如果将同步I/O方式下的数据传输比做数据传输的零星方式(这里的零星是指在数据传输的过程中是以零星的字节方式进行的),那么就可以将非阻塞I/O方式下的数据传输比做数据传输的集装箱方式(在字节和低层数据传输之间,多了一层缓冲区,因此,可以将缓冲区看做是装载字节的集装箱)。大家可以想象,如果我们要运送比较少的货物,用集装箱好象有点不太合算,而如果要运送上百吨的货物,用集装箱来运送的成本会更低。在数据传输过程中也是一样,如果数据量很小时,使用同步I/O方式会更适合,如果数据量很大时(一般以G为单位),使用非阻塞I/O方式的效率会更高。因此,从理论上说,数据量越大,使用非阻塞I/O方式的单位成本就会越低。产生这种结果的原因和缓冲区的一些特性有着直接的关系。在本节中,将对缓冲区的一些主要特性进行讲解,使读者可以充分理解缓冲区的概念,并能通过缓冲区来提高程序的执行效率。

创建缓冲区

Java提供了七个基本的缓冲区,分别由七个类来管理,它们都可以在java.nio包中找到。这七个类如下所示:

  • ByteBuffer
  • ShortBuffer
  • IntBuffer
  • CharBuffer
  • FloatBuffer
  • DoubleBuffer
  • LongBuffer
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <meta name="ProgId" content="Word.Document"> <meta name="Generator" content="Microsoft Word 11"> <meta name="Originator" content="Microsoft Word 11"> <link rel="File-List" href="file:///C:%5CDOCUME%7E1%5CADMINI%7E1%5CLOCALS%7E1%5CTemp%5Cmsohtml1%5C01%5Cclip_filelist.xml"> <!--[if gte mso 9]><xml> Normal 0 7.8 磅 0 2 false false false MicrosoftInternetExplorer4 </xml><![endif]--><!--[if gte mso 9]><![endif]--><!--[if !mso]> <style> st1":*{behavior:url(#ieooui) } </style> <![endif]--><style> <!-- /* Font Definitions */ &#64;font-face {font-family:宋体; panose-1:2 1 6 0 3 1 1 1 1 1;} &#64;font-face {font-family:""&#64;宋体"; panose-1:2 1 6 0 3 1 1 1 1 1;} /* Style Definitions */ p.MsoNormal, li.MsoNormal, div.MsoNormal {mso-style-parent:""; margin:0cm; margin-bottom:.0001pt; text-align:justify; text-justify:inter-ideograph; font-size:10.5pt; font-family:"Times New Roman";} /* Page Definitions */ &#64;page {} &#64;page Section1 {size:612.0pt 792.0pt; margin:72.0pt 90.0pt 72.0pt 90.0pt;} div.Section1 {page:Section1;} /* List Definitions */ &#64;list l0 {} &#64;list l0:level1 { margin-left:40.5pt; text-indent:-19.5pt;} &#64;list l1 {} &#64;list l1:level1 { margin-left:39.0pt; text-indent:-18.0pt;} ol {margin-bottom:0cm;} ul {margin-bottom:0cm;} --> </style> <!--[if gte mso 10]> <style> /* Style Definitions */ table.MsoNormalTable { mso-style-parent:""; font-size:10.0pt; font-family:"Times New Roman";} </style> <![endif]-->

这七个类中的方法类似,只是它们的返回值或参数和相应的简单类型相对应,如ByteBuffer类的get方法返回了byte类型的数据,而put方法需要一个byte类型的参数。在CharBuffer类中的getput方法返回和传递的数据类型就是char。这七个类都没有public构造方法,因此,它们不能通过new来创建相应的对象实例。这些类都可以通过两种方式来创建相应的对象实例。

1. 通过静态方法allocate来创建缓冲区。

这七类都有一个静态的allocate方法,通过这个方法可以创建有最大容量限制的缓冲区对象。allocate的定义如下:

ByteBuffer类中的allocate方法:

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->publicstaticByteBufferallocate(intcapacity)

IntBuffer类中的allocate方法:

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->publicstaticIntBufferallocate(intcapacity)

其他五个缓冲区类中的allocate 方法定义和上面的定义类似,只是返回值的类型是相应的缓冲区类。

allocate方法有一个参数capacity,用来指定缓冲区容量的最大值。capacity的不能小于0,否则会抛出一个IllegalArgumentException异常。使用allocate来创建缓冲区,并不是一下子就分配给缓冲区capacity大小的空间,而是根据缓冲区中存储数据的情况来动态分配缓冲区的大小(实际上,在低层Java采用了数据结构中的堆来管理缓冲区的大小),因此,这个capacity可以是一个很大的值,如1024*10241M)。allocate的使用方法如下:

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->ByteBufferbyteBuffer=ByteBuffer.allocate(1024);
IntBufferintBuffer
=IntBuffer.allocate(1024);

在使用allocate创建缓冲区时应用注意,capacity的含义随着缓冲区的不同而不同。如创建字节缓冲区时,capacity指的是字节数。而在创建整型(int)缓冲区时,capacity指的是int型值的数目,如果转换成字数,capacity的值应该乘4。如上面代码中的intBuffer缓冲区最大可容纳的字节数是1024*4 = 4096个。

2. 通过静态方法wrap来创建缓冲区。

使用allocate方法可以创建一个空的缓冲区。而wrap方法可以利用已经存在的数据来创建缓冲区。wrap方法可以将数组直接转换成相应类型的缓冲区。wrap方法有两种重载形式,它们的定义如下:

ByteBuffer类中的wrap方法:

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->publicstaticByteBufferwrap(byte[]array)
publicstaticByteBufferwrap(byte[]array,intoffset,intlength)

IntBuffer类中的wrap方法:

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->publicstaticIntBufferwrap(byte[]array)
publicstaticIntBufferwrap(byte[]array,intoffset,intlength)

其他五个缓冲区类中的wrap 方法定义和上面的定义类似,只是返回值的类型是相应的缓冲区类。

wrap方法中的array参数是要转换的数组(如果是其他的缓冲区类,数组的类型就是相应的简单类型,如IntBuffer类中的wrap方法的array就是int[]类型)。offset是要转换的子数组的偏移量,也就是子数组在array中的开始索引。length是要转换的子数组的长度。利用后两个参数可以将array数组中的一部分转换成缓冲区对象。它们的使用方法如下:

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->byte[]myByte=newbyte[]{1,2,3};
int[]myInt=newint[]{1,2,3,4};
ByteBufferbyteBuffer
=ByteBuffer.wrap(myByte);
IntBufferintBuffer
=IntBuffer.wrap(myInt,1,2);

可以通过缓冲区类的capacity方法来得到缓冲区的大小。capacity方法的定义如下:

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->publicfinalintcapacity()

如果使用allocate方法来创建缓冲区,capacity方法的返回值就是capacity参数的值。而使用wrap方法来创建缓冲区,capacity方法的返回值是array数组的长度,但要注意,使用wrap来转换array的字数组时,capacity的长度仍然是原数组的长度,如上面代码中的intBuffer缓冲区的capacity值是4,而不是2

除了可以将数组转换成缓冲区外,也可以通过缓冲区类的array方法将缓冲区转换成相应类型的数组。IntBuffer类的array方法的定义方法如下(其他缓冲区类的array的定义类似):

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->publicfinalint[]array()

下面的代码演示了如何使用array方法将缓冲区转换成相应类型的数组。


<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->int[]myInt=newint[]{1,2,3,4,5,6};
IntBufferintBuffer
=IntBuffer.wrap(myInt,1,3);
for(intv:intBuffer.array())
System.out.print(v
+"");

在执行上面代码后,我们发现输出的结果是1 2 3 4 5 6,而不是2 3 4。这说明在将子数组转换成缓冲区的过程中实际上是将整个数组转换成了缓冲区,这就是用wrap包装子数组后,capacity的值仍然是原数组长度的真正原因。在使用array方法时应注意,在以下两种缓冲区中不能使用array方法:

  • 只读的缓冲区

如果使用只读缓冲区的array方法,将会抛出一个ReadOnlyBufferException异常。

  • 使用allocateDirect方法创建的缓冲区

如果调用这种缓冲区中的array方法,将会抛出一个UnsupportedOperationException异常。

可以通过缓冲区类的hasArray方法来判断这个缓冲区是否可以使用array方法,如果返回true,则说明这个缓冲区可以使用array方法,否则,使用array方法将会抛出上述的两种异常之一。

注意: 使用array方法返回的数组并不是缓冲区数据的副本。被返回的数组实际上就是缓冲区中的数据,也就是说,array方法只返回了缓冲区数据的引用。当数组中的数据被修改后,缓冲区中的数据也会被修改,返之也是如此。关于这方面内容将在下一节“读写缓冲区中的数据”中详细讲解。

<meta http-equiv="Content-Type" content="text/html; charset=utf-8"><meta name="ProgId" content="Word.Document"><meta name="Generator" content="Microsoft Word 11"><meta name="Originator" content="Microsoft Word 11"><link rel="File-List" href="file:///C:%5CDOCUME%7E1%5CADMINI%7E1%5CLOCALS%7E1%5CTemp%5Cmsohtml1%5C01%5Cclip_filelist.xml"><!--[if gte mso 9]><xml> Normal 0 7.8 磅 0 2 false false false MicrosoftInternetExplorer4 </xml><![endif]--><!--[if gte mso 9]><![endif]--><style> <!-- /* Font Definitions */ &#64;font-face {font-family:Wingdings; panose-1:5 0 0 0 0 0 0 0 0 0;} &#64;font-face {font-family:宋体; panose-1:2 1 6 0 3 1 1 1 1 1;} &#64;font-face {font-family:""&#64;宋体"; panose-1:2 1 6 0 3 1 1 1 1 1;} /* Style Definitions */ p.MsoNormal, li.MsoNormal, div.MsoNormal {mso-style-parent:""; margin:0cm; margin-bottom:.0001pt; text-align:justify; text-justify:inter-ideograph; font-size:10.5pt; font-family:"Times New Roman";} /* Page Definitions */ &#64;page {} &#64;page Section1 {size:612.0pt 792.0pt; margin:72.0pt 90.0pt 72.0pt 90.0pt;} div.Section1 {page:Section1;} /* List Definitions */ &#64;list l0 {} &#64;list l0:level1 { margin-left:42.75pt; text-indent:-21.0pt; font-family:Wingdings;} ol {margin-bottom:0cm;} ul {margin-bottom:0cm;} --> </style><!--[if gte mso 10]> <style> /* Style Definitions */ table.MsoNormalTable { mso-style-parent:""; font-size:10.0pt; font-family:"Times New Roman";} </style> <![endif]-->

在上述的七个缓冲区类中,ByteBuffer类和CharBuffer类各自还有另外一种方法来创建缓冲区对象。

l ByteBuffer

可以通过ByteBuffer类的allocateDirect方法来创建ByteBuffer对象。allocateDirect方法的定义如下:

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->publicstaticByteBufferallocateDirect(intcapacity)

使用allocateDirect方法可以一次性分配capacity大小的连续字节空间。通过allocateDirect方法来创建具有连续空间的ByteBuffer对象虽然可以在一定程度上提高效率,但这种方式并不是平台独立的。也就是说,在一些操作系统平台上使用allocateDirect方法来创建ByteBuffer对象会使效率大幅度提高,而在另一些操作系统平台上,性能会表现得非常差。而且allocateDirect方法需要较长的时间来分配内存空间,在释放空间时也较慢。因此,在使用allocateDirect方法时应谨慎。

通过isDirect方法可以判断缓冲区对象(其他的缓冲区类也有isDirect方法,因为,ByteBuffer对象可以转换成其他的缓冲区对象,这部分内容将在后面讲解)是用哪种方式创建的,如果isDirect方法返回true,则这个缓冲区对象是用allocateDirect方法创建的,否则,就是用其他方法创建的缓冲区对象。

l CharBuffer

我们可以发现,上述的七种缓冲区中并没有字符串缓冲区,而字符串在程序中却是最常用的一种数据类型。不过不要担心,虽然java.nio包中并未提供字符串缓冲区,但却可以将字符串转换成字符缓冲区(就是CharBuffer对象)。在CharBuffer类中的wrap方法除了上述的两种重载形式外,又多了两种重载形式,它们的定义如下:

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->publicstaticCharBufferwrap(CharSequencecsq)
publicstaticCharBufferwrap(CharSequencecsq,intstart,intend)

其中csq参数表示要转换的字符串,但我们注意到csq的类型并不是String,而是CharSequenceCharSequenceJava中四个可以表示字符串的类的父类,这四个类是StringStringBufferStringBuilderCharBuffer(大家要注意,StringBuffer和本节讲的缓冲区类一点关系都没有,这个类在java.lang包中)。也就是说,CharBuffer类的wrap方法可以将这四个类的对象转换成CharBuffer对象。

另外两个参数startend分别是子字符串的开始索引和结束索引的下一个位置,如将字符串"1234"中的"23" 转换成CharBuffer对象的语句如下:

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->CharBuffercb=CharBuffer.wrap("1234",1,3);

下面的代码演示了如何使用wrap方法将不同形式的字符串转换成CharBuffer对象。

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->StringBufferstringBuffer=newStringBuffer("通过StringBuffer创建CharBuffer对象");
StringBuilderstringBuilder
=newStringBuilder("通过StringBuilder创建CharBuffer对象");
CharBuffercharBuffer1
=CharBuffer.wrap("通过String创建CharBuffer对象");
CharBuffercharBuffer2
=CharBuffer.wrap(stringBuffer);
CharBuffercharBuffer3
=CharBuffer.wrap(stringBuilder);
CharBuffercharBuffer4
=CharBuffer.wrap(charBuffer1,1,3);

下一篇:Java网络编程从入门到精通(34):读写缓冲区中的数据---使用get和put方法按顺序读写单个数据



国内最棒的Google Android技术社区(eoeandroid),欢迎访问!

《银河系列原创教程》发布

《Java Web开发速学宝典》出版,欢迎定购

分享到:
评论

相关推荐

    芯片I/O缓冲及ESD电路设计

     针对引脚的输入输出缓冲(I/O buffer)电路设计,也可以称为输入输出接口(I/O interface)电路设计,是一颗完整芯片设计中不可或缺的组成部分,但是详细论述其设计规则的文章或者著作在国内却比较鲜见,这对初学...

    Linux编程从入门到精通

    第9章 I/O端口编程 307 9.1 鼠标编程 307 9.2 调制解调器编程 308 9.3 打印机编程 308 9.4 游戏杆编程 308 第10章 把应用程序移植到Linux上 309 10.1 介绍 309 10.2 信号处理 309 10.2.1 SVR4、BSD和POSIX.1下 的...

    环形缓冲区 封装模板类

    | 插入环形缓冲区:bool CircleBuffer&lt;类型名&gt;::push(Elem) //将Elem插入到缓冲区尾部,若缓冲区已满则返回false | 取出首元素: bool CircleBuffer&lt;类型名&gt;::pop(&Elem) //取出首元素赋值给Elem,若缓冲区已空则...

    I/O缓冲池演示程序

    操作系统中的I/O管理,I/O缓冲池程序,使用C++编写,Windows环境

    C游戏编程从入门到精通(13M)

    C游戏编程从入门到精通 本书以C语言游戏编程入手,以102个实例,近200个函数较为系统地介绍了C基于游戏编程与开发的方法与技巧,内容丰富并相互包容,相互渗透。以实际的基于不同平台的游戏制作为背景,知识阐述...

    浅析标准I/O缓冲区

     学习过编程的朋友都知道ANSI C里定义的标准I/O是一种带缓冲的磁盘I/O,目的是尽可能减少使用read和write系统调用的次数,从而提高I/O效率。标准I/O提供了3种类型的缓冲类型。  ● 全缓冲。在这种情况下,当填满...

    Linux I/O 原理和 Zero-copy 技术全面揭秘

    两万字长文从虚拟内存、I/O 缓冲区,用户态&内核态以及 I/O 模式等等知识点全面而又详尽地剖析 Linux 系统的 I/O 底层原理,分析了 Linux 传统的 I/O 模式的弊端,进而引入 Linux Zero-copy 零拷贝技术的介绍和原理...

    单片机的一些缩写.doc

    IE:interrupt enable // 中断使能 IP:interrupt priority //中断优先级 PCON:power control //电源控制 SCON:serial control //串行口控制 SBUF:serial buffer //串行数据缓冲 TCON:timer control //定时器控制 TMOD...

    JSP 程序设计从入门到精通 PDF 教程

    《JSP程序设计从入门到精通》电子书  第1篇 入门篇 7  第1章 Jsp概述 技术分析 7  1.1 Jsp简介与历史背景 7  1.1.1 日新月异的Web技术 7  1.1.2 什么是JSP 8  1.1.3 JSP技术有以下几个显著的优点 9  ...

    EDA/PLD中的浅析标准I/O缓冲区

     学习过编程的朋友都知道ANSI C里定义的标准I/O是一种带缓冲的高级磁盘I/O,目的是尽可能减少使用read和write系统调用的次数,从而提高I/O效率。标准I/O提供了3种类型的缓冲类型。  ● 全缓冲。在这种情况下,当...

    《C游戏编程从入门到精通》光盘源代码

    第一章 文本模式游戏 第二章 图形图像绘制 第三章 简单动画实现 第四章 简单图形游戏 ...第十三章 内存缓冲技术 第十四章 接口与通信技术 第十五章 界面技术 第十六章 编程艺术及其他问题 第十七章 游戏实例设计

    Java Web编程宝典-十年典藏版.pdf.part2(共2个)

    Java Web编程宝典-十年典藏版.pdf 是PDF电子书,不是源码。共分2个包。 《Java Web编程宝典(十年典藏版)》是一本集技能、范例、项目和应用为一体的学习手册,书中介绍了应用Java Web进行程序开发的各种技术、技巧。...

    buffer应用缓冲区

    buffer应用缓冲区 socket应用层

    Java入门缓冲区溢出编程心得

    Java入门缓冲区溢出编程心得

    快播网页插件,jQuery QVOD

    //NextQvod: "", /*下一集资源地址,预缓冲时使用*/ //ShowControl: "1", /*是否显示控制栏,0=不显示 1= 显示 默认参数是显示*/ //AdUrl: "http://buffer-ad.qvod.com/" /*缓冲广告 注:3.0.0.58及将来...

    Cesium 绘制缓冲区 点 线 面缓冲区

    Cesium 绘制缓冲区 点 线 面缓冲区 预览:http://dongnan185.com:8083/videos/draw.mp4 启动:npm i npm run serve

    总是看起来像平面数组的C ++环形缓冲区-C/C++开发

    内容尽管有名称,但该存储库包含两个不同的环形缓冲区的实现:线性环形缓冲区:include / bev / linear_ringbuffer.hpp IO缓冲区:include / bev / io_buffer.hpp此顶级自述文件主要由目录提供。尽管有名称,该存储...

    buffer-type:从缓冲区数据检测内容类型

    缓冲区类型 从缓冲区数据中检测内容类型。安装$ npm install buffer-type用法const bt = require ( 'buffer-type' ) ;const fs = require ( 'fs' ) ;const info = bt ( fs . readFileSync ( __dirname + '/logo.png'...

    取得硬盘序列号

    //获得硬盘序列号 function GetIdeSerialNumber: pchar; const IDENTIFY_BUFFER_SIZE = 512;... // 用于保存从驱动器读出的数据的缓冲区,实际长度由cBufferSize决定 bBuffer: array[0..0] of BYTE; end;

Global site tag (gtag.js) - Google Analytics