`

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

    博客分类:
  • Java
阅读更多

在家上网赚钱更容易

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

创建缓冲区

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

  •  ByteBuffer   
  • ShortBuffer
  • IntBuffer
  • CharBuffer
  • FloatBuffer
  • DoubleBuffer
  • LongBuffer

 

这七个类中的方法类似,只是它们的返回值或参数和相应的简单类型相对应,如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 /> -->public static ByteBuffer allocate(int capacity)

IntBuffer类中的allocate方法:

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->public static IntBuffer allocate(int capacity)

其他五个缓冲区类中的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 /> -->ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
IntBuffer intBuffer 
= 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 /> -->public static ByteBuffer wrap(byte[] array)
public static ByteBuffer wrap(byte[] array, int offset, int length)

IntBuffer类中的wrap方法:

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->public static IntBuffer wrap(byte[] array)
public static IntBuffer wrap(byte[] array, int offset, int length)

其他五个缓冲区类中的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 = new byte[] { 123 };
int[] myInt = new int[] { 1234 };
ByteBuffer byteBuffer 
= ByteBuffer.wrap(myByte);
IntBuffer intBuffer 
= IntBuffer.wrap(myInt, 12);

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

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

如果使用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 /> -->public final int[] array()

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

 

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->int[] myInt = new int[] { 123456 };
IntBuffer intBuffer 
= IntBuffer.wrap(myInt, 13);
for (int v : 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方法只返回了缓冲区数据的引用。当数组中的数据被修改后,缓冲区中的数据也会被修改,返之也是如此。关于这方面内容将在下一节“读写缓冲区中的数据”中详细讲解。

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

l         ByteBuffer

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

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->public static ByteBuffer allocateDirect(int capacity)

 

使用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 /> -->public static CharBuffer wrap(CharSequence csq)
public static CharBuffer wrap(CharSequence csq, int start, int end)

 

其中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 /> -->CharBuffer cb = CharBuffer.wrap("1234"13);

 

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

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->StringBuffer stringBuffer = new StringBuffer("通过StringBuffer创建CharBuffer对象");
StringBuilder stringBuilder 
= new StringBuilder("通过StringBuilder创建CharBuffer对象");
CharBuffer charBuffer1 
= CharBuffer.wrap("通过String创建CharBuffer对象");
CharBuffer charBuffer2 
= CharBuffer.wrap(stringBuffer);
CharBuffer charBuffer3 
= CharBuffer.wrap(stringBuilder);
CharBuffer charBuffer4 
= CharBuffer.wrap(charBuffer1, 13);

 

在家上网赚钱更容易

分享到:
评论

相关推荐

    java阻塞i/o与非阻塞i/o控制

    了解并熟练掌握这两种I/O模型,有助于开发者编写出高效、可扩展的Java应用程序,特别是在网络编程和服务器开发中,非阻塞I/O能够带来显著的性能提升。因此,深入学习和实践Java中的阻塞I/O与非阻塞I/O控制是非常必要...

    Java网络编程从入门到精通

    5. **NIO(非阻塞I/O)**:Java NIO(New IO)是自Java 1.4引入的新API,它提供了选择器、通道和缓冲区等机制,可以提高网络编程的效率和性能。 6. **HTTP客户端库**:Java提供了HttpURLConnection来处理HTTP请求,...

    JAVA网络编程从入门到精通

    ### JAVA网络编程从入门到精通知识点详解 #### 一、Internet地址概述 互联网中的每一台设备都需要有一个唯一的标识符——IP地址。当前广泛使用的IPv4地址由四个字节组成,而未来的趋势是采用16个字节的IPv6地址。 ...

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

    在电子设计领域,芯片I/O缓冲及ESD电路设计是至关重要的环节,它们关系到芯片与外界环境的交互效率和稳定性。I/O缓冲电路作为芯片与外部系统通信的桥梁,承担着信号转换、驱动和保护的重要任务。这篇文章将深入探讨I...

    I/O缓冲池演示程序

    4. 请求调度:根据I/O请求的优先级和缓冲区的状态,决定哪个请求应先执行,以及数据应从哪个缓冲区传输。 5. 错误处理:捕获并处理可能出现的I/O错误,确保程序的健壮性。 在实际应用中,I/O缓冲池不仅可以用于磁盘...

    Java I/O, 2nd Edition

    这本书在第二版中对Java I/O进行了更新,涵盖了从Java 5到Java 8的最新发展,包括NIO.2(New I/O 2)框架的介绍。 1. **Java I/O基础**:书中首先介绍了Java I/O的基本概念,如流、缓冲区、字符编码和文件操作。流...

    Java I/O编程 java

    Java I/O 编程是Java开发中的重要组成部分,主要用于处理数据的输入与输出。下面将详细阐述其中的关键概念和方法。 1. 数据流的概念及输入输出方法: 数据流是计算机中进行数据传输的通道,包括从外部设备到程序的...

    Java 新I/O

    Java 新I/O,也称为NIO(New Input/Output),是Java平台中对传统I/O模型的一种改进。在Java 1.4版本中引入的NIO库为开发人员提供了更高效、非阻塞的数据处理方式,特别适用于高并发、低延迟的系统。NIO的核心在于...

    怎么使用I/O编程???

    在Java编程中,I/O(Input/Output)处理是与外部世界交互的关键技术,涉及文件读写、网络通信等场景。I/O的核心思想是通过流(Stream)来传输数据,使得程序能从数据源读取数据或将数据写入目标。 **1.1 I/O简介** I...

    jdk6.0从入门到精通-----chapter5网络编程 新I/O(含源码下载)

    在Java编程领域,JDK(Java Development Kit)是开发和运行Java应用程序的...总的来说,"JDK6.0从入门到精通-----chapter5网络编程 新I/O"是一个极好的学习资源,无论你是初学者还是有经验的开发者,都能从中获益良多。

    Java网络编程.pdf

    ### Java网络编程精要 #### Internet地址概述与分类 ...以上是对Java网络编程中一些核心知识点的概括,涵盖了从IP地址管理、套接字通信到非阻塞I/O模型的各个方面,为深入理解和应用Java网络编程技术奠定了基础。

    魔乐科技:从入门到精通Java全部源码

    4. **输入输出流**:IO流的基本概念,字符流和字节流,缓冲流,以及NIO(非阻塞I/O)的概念。 5. **线程编程**:线程的创建方式,同步机制(synchronized,wait(),notify(),锁),线程池的使用。 6. **反射机制*...

    Java 非阻塞I/O使用方法

    Java 非阻塞I/O使用方法 Java 非阻塞I/O是处理高并发的一种手段,在高并发的情况下,创建和回收线程以及在线程间切换的开销变得不容忽视,此时就可以使用非阻塞I/O技术。这种技术的核心思想是每次选取一个准备好的...

    Java网络编程期末考试复习题库+答案

    在Java中,网络编程主要依赖于Java的Socket编程、ServerSocket、URL类以及NIO(非阻塞I/O)等核心API。这份"Java网络编程期末考试复习题库+答案"为学生提供了全面的复习资源,涵盖了Java网络编程的主要知识点。 1. ...

    Java I/O, NIO and NIO.2

    非阻塞I/O(Non-blocking I/O),简称NIO,是Java 1.4引入的一个重要特性,主要由java.nio包提供。NIO的核心在于通道(Channels)和缓冲区(Buffers)。通道类似于流,但它们支持非阻塞读写,这意味着当数据不可用时...

    buffer应用缓冲区

    同时,为了确保数据的完整性和一致性,需要正确处理缓冲区满或空的情况,例如采用非阻塞I/O或信号驱动I/O。 总的来说,Buffer在应用缓冲区和Socket通信中的应用是提高系统效率、优化数据处理的关键。了解并掌握...

    《Java网络编程实例:Java网络编程实例》

    7. **NIO(非阻塞I/O)**:Java NIO(New I/O)提供了一种更高效的数据处理方式,特别是在处理大量并发连接时。通道(Channels)和缓冲区(Buffers)是NIO的核心概念。 8. **HTTP协议**:作为应用层最常用的协议之...

    深入分析 Java I/O 的工作机制(转载)

    Java I/O(输入/输出)系统是Java编程语言中用于处理数据流的重要组成部分,它允许程序与外部资源如文件、网络、硬件设备等进行交互。深入理解Java I/O的工作机制对于开发高效、可靠的系统至关重要。以下是对Java I/...

    精通java网络编程

    五、NIO(非阻塞I/O) Java的NIO(New I/O)库提供了一种更高效的I/O模型,它支持选择器(Selector)和通道(Channel),可以在单个线程中处理多个连接,减少了线程创建和销毁的开销。Selector可以监控多个Channel,...

    java从入门到精通(第三版)光盘实例

    《Java从入门到精通》(第三版)是一本旨在帮助初学者和有一定基础的程序员深入理解Java编程语言的书籍。光盘实例作为该书的重要补充,提供了丰富的代码示例和实际应用,帮助读者巩固理论知识并提升实战技能。在这些...

Global site tag (gtag.js) - Google Analytics