`

Java nio 笔记:系统IO、缓冲区、流IO、socket通道

 
阅读更多

http://www.cnblogs.com/549294286/p/3761480.html

 

一、Java IO 和 系统 IO 不匹配

  在大多数情况下,Java 应用程序并非真的受着 I/O 的束缚。操作系统并非不能快速传送数据,让 Java 有事可做;相反,是 JVM 自身在 I/O 方面效率欠佳。操作系统与 Java 基于流的 I/O模型有些不匹配。操作系统要移动的是大块数据(缓冲区),这往往是在硬件直接存储器存取(DMA)的协助下完成的。而 JVM 的 I/O 操作类喜欢操作小块数据——单个字节、几行文本。结果,操作系统送来整缓冲区的数据,java.io 包的流数据类再花大量时间把它们拆成小块,往往拷贝一个小块就要往返于几层对象。操作系统喜欢整卡车地运来数据,java.io 类则喜欢一铲子一铲子地加工数据。有了 NIO,就可以轻松地把一卡车数据备份到您能直接使用的地方(ByteBuffer 对象)。

  这并不是说使用传统的 I/O 模型无法移动大量数据——当然可以(现在依然可以)。具体地说,RandomAccessFile类在这方面的效率就不低,只要坚持使用基于数组的read()write()方法。

  这些方法与底层操作系统调用相当接近,尽管必须保留至少一份缓冲区拷贝。

 

  为了解决这一问题,java.nio 软件包提供了新的抽象。具体地说,就是 ChannelSelector类。

 

二、 缓冲区

(一)缓冲区操作

缓冲区,以及缓冲区如何工作,是所有 I/O 的基础。所谓“输入/输出”讲的无非就是把数据移进或移出缓冲区。

进程执行 I/O 操作,归结起来,也就是向操作系统发出请求,让它要么把缓冲区里的数据排干(写),要么用数据把缓冲区填满(读)。进程使用这一机制处理所有数据进出操作。操作系统内部处理这一任务的机制,其复杂程度可能超乎想像,但就概念而言,却非常直白易懂。图 1-1 简单描述了数据从外部磁盘向运行中的进程的内存区域移动的过程。进程使用read()系统调用,要求其缓冲区被填满。内核随即向磁盘控制硬件发出命令,要求其从磁盘读取数据。磁盘控制器把数据直接写入内核内存缓冲区,这一步通过 DMA 完成,无需主CPU协助。一旦磁盘控制器把缓冲区装满,内核即把数据从内核空间的临时缓冲区拷贝到进程执行read()调用时指定的缓冲区。


图 1-1. I/O 缓冲区操作简图

JVM 就是常规进程,驻守于用户空间。用最重要的是,所有 I/O 都直接或间接通过内核空间。当进程请求 I/O 操作的时候,它执行一个系统调用(有时称为陷阱)将控制权移交给内核。C/C++程序员所熟知的底层函数open()read()write()close()要做的无非就是建立和执行适当的系统调用。当内核以这种方式被调用,它随即采取任何必要步骤,找到进程所需数据,并把数据传送到用户空间内的指定缓冲区。内核试图对数据进行高速缓存或预读取,因此进程所需数据可能已经在内核空间里了。如果是这样,该数据只需简单地拷贝出来即可。如果数据不在内核空间,则进程被挂起,内核着手把数据读进内存。

为什么不直接让磁盘控制器把数据送到用户空间的缓冲区呢?这样做有几个问题。首先,硬件通常不能直接访问用户空间。其次,像磁盘这样基于块存储的硬件设备操作的是固定大小的数据块,而用户进程请求的可能是任意大小的或非对齐的数据块。在数据往来于用户空间与存储设备的过程中,内核负责数据的分解、再组合工作,因此充当着中间人的角色。

 

(二) 发散/汇聚

许多操作系统能把组装/分解过程进行得更加高效。根据发散/汇聚的概念,进程只需一个系统调用,就能把一连串缓冲区地址传递给操作系统。然后,内核就可以顺序填充或排干多个缓冲区,读的时候就把数据发散到多个用户空间缓冲区,写的时候再从多个缓冲区把数据汇聚起来(图1-2)。


图 1-2. 三个缓冲区的发散读操作

这样用户进程就不必多次执行系统调用(那样做可能代价不菲),内核也可以优化数据的处理过程,因为它已掌握待传输数据的全部信息。如果系统配有多个 CPU,甚至可以同时填充或排干多个缓冲区。

 

 

三、 流I/O

并非所有 I/O 都像前几节讲的是面向块的,也有流 I/O,其原理模仿了通道。I/O 字节流必须顺序存取,常见的例子有TTY(控制台)设备、打印机端口和网络连接。

流的传输一般(也不必然如此)比块设备慢,经常用于间歇性输入。多数操作系统允许把流置于非块模式,这样,进程可以查看流上是否有输入,即便当时没有也不影响它干别的。这样一种能力使得进程可以在有输入的时候进行处理,输入流闲置的时候执行其他功能。

比非块模式再进一步,就是就绪性选择。就绪性选择与非块模式类似(常常就是建立在非块模式之上),但是把查看流是否就绪的任务交给了操作系统。操作系统受命查看一系列流,并提醒进程哪些流已经就绪。这样,仅仅凭借操作系统返回的就绪信息,进程就可以使用相同代码和单一线程,实现多活动流的多路传输。这一技术广泛用于网络服务器领域,用来处理数量庞大的网络连接。就绪性选择在大容量缩放方面是必不可少的。

 

四、Socket通道

DatagramChannelSocketChannel实现定义读和写功能的接口而ServerSocketChannel不实现。ServerSocketChannel负责监听传入的连接和创建新的SocketChannel对象,它本身从不传输数据。

socket和socket通道之间的关系。之前的章节中有写道,通道是一个连接I/O服务导管并提供与该服务交互的方法。就某个socket而言,它不会再次实现与之对应的socket通道类中的socket协议 API,而java.net中已经存在的socket通道都可以被大多数协议操作重复使用。

全部socket通道类(DatagramChannelSocketChannelServerSocketChannel)在被实例化时都会创建一个对等socket对象。这些是我们所熟悉的来自java.net的类(SocketServerSocketDatagramSocket),它们已经被更新以识别通道。对等socket可以通过调用socket()方法从一个通道上获取。此外,这三个java.net类现在都有getChannel()方法。

 

虽然每个socket通道(在java.nio.channels包中)都有一个关联的java.net socket对象,却并非所有的socket都有一个关联的通道。如果您用传统方式(直接实例化)创建了一个Socket对象,它就不会有关联的SocketChannel并且它的getChannel()方法将总是返回null

 

五、ServerSocketChannel

让我们从最简单的ServerSocketChannel来开始对socket通道类的讨论。以下是ServerSocketChannel的完整 API:

复制代码
public abstract class ServerSocketChannel extends AbstractSelectableChannel {
    public static ServerSocketChannel open() throws IOException
    public abstract ServerSocket socket();
    public abstract ServerSocket accept() throws IOException;
    public final int validOps()
}
复制代码

ServerSocketChannel是一个基于通道的socket监听器。它同我们所熟悉的java.net.ServerSocket执行相同的基本任务,不过它增加了通道语义,因此能够在非阻塞模式下运行。

用静态的open()工厂方法创建一个新的ServerSocketChannel对象,将会返回同一个未绑定的java.net.ServerSocket关联的通道。该对等ServerSocket可以通过在返回的ServerSocketChannel上调用socket()方法来获取。作为ServerSocketChannel的对等体被创建的ServerSocket对象依赖通道实现。这些socket关联的SocketImpl能识别通道。通道不能被封装在随意的socket对象外面。

 

由于ServerSocketChannel没有bind()方法,因此有必要取出对等的socket并使用它来绑定到一个端口以开始监听连接。我们也是使用对等ServerSocket的API来根据需要设置其他的socket选项。

ServerSocketChannel ssc = ServerSocketChannel.open();
ServerSocket serverSocket = ssc.socket();
// 监听端口1234
serverSocket.bind (new InetSocketAddress(1234));

同它的对等体java.net.ServerSocket一样,ServerSocketChannel也有accept()方法。一旦您创建了一个ServerSocketChannel并用对等socket绑定了它,然后您就可以在其中一个上调用accept()。如果您选择在ServerSocket上调用accept()方法,那么它会同任何其他的ServerSocket表现一样的行为:总是阻塞并返回一个java.net.Socket对象。如果您选择在ServerSocketChannel上调用accept()方法则会返回SocketChannel类型的对象,返回的对象能够在非阻塞模式下运行。假设系统已经有一个安全管理器(security manager),两种形式的方法调用都执行相同的安全检查。

 

如果以非阻塞模式被调用,当没有传入连接在等待时,ServerSocketChannel.accept()会立即返回null。正是这种检查连接而不阻塞的能力实现了可伸缩性并降低了复杂性。可选择性也因此得到实现。我们可以使用一个选择器实例来注册一个ServerSocketChannel 对象以实现新连接到达时自动通知的功能。例 3-7 演示了如何使用一个非阻塞的accept()方法:

复制代码
/*
 *例 3-7 使用ServerSocketChannel的非阻塞accept()方法
 */
package com.ronsoft.books.nio.channels;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.net.InetSocketAddress;
/**
* Test nonblocking accept() using ServerSocketChannel.
* Start this program, then "telnet localhost 1234" to
* connect to it.
*
* @author Ron Hitchens (ron@ronsoft.com)
*/
public class ChannelAccept {
    public static final String GREETING = "Hello I must be going.\r\n";
    
    public static void main (String [] argv) throws Exception {
        int port = 1234; //默认端口
        if (argv.length > 0) {
            port = Integer.parseInt(argv[0]);
        }
        ByteBuffer buffer = ByteBuffer.wrap (GREETING.getBytes());
        ServerSocketChannel ssc = ServerSocketChannel.open();
        ssc.socket().bind (new InetSocketAddress(port));
        ssc.configureBlocking(false);
        while(true) {
            System.out.println ("Waiting for connections");
            SocketChannel sc = ssc.accept();
            if (sc == null) {
                // no connections, snooze a while
                Thread.sleep (2000);
            } else {
                System.out.println ("Incoming connection from: " + sc.socket().getRemoteSocketAddress());
                buffer.rewind();
                sc.write(buffer);
                sc.close();
            }
        }
    }
}
复制代码

前面列出的最后一个方法validOps()是同选择器一起使用的。

分享到:
评论

相关推荐

    java io流学习笔记1

    Java IO流是Java平台中用于处理输入输出的重要组成部分,它为数据在不同设备间传输提供了高效、灵活的方式。本文将深入探讨Java IO流的基本概念、分类、使用方法以及相关工具,帮助你更好地理解和掌握这一核心技能。...

    IO-黑马程序员Java学习笔记.rar

    这些可能涵盖了高级IO特性,如NIO(New IO,Java 1.4引入)和NIO.2(Java 7引入),包括选择器(Selector)、通道(Channel)、缓冲区(Buffer)和文件属性的处理。还有可能涉及网络IO,如Socket和ServerSocket的...

    JAVA学习笔记

    NIO包括通道(Channels)、缓冲区(Buffers)和选择器(Selectors),通过选择器,一个线程可以同时监控多个通道的事件,提高效率。文件“2013-12-26-java-newIO.txt”和“2013-12-27-java-newIO续.txt”应该会详细...

    阿里P8 架构师整理Java学习笔记.pdf

    - **JAVA NIO**: 提供了基于通道(Channel)和缓冲区(Buffer)的新的IO处理方式,提高了IO操作的性能和灵活性。 - **NIO的缓冲区**: 用于存储不同数据类型的临时容器,包括ByteBuffer、CharBuffer等。 - **NIO的非...

    JAVA源码笔记

    11. **IO流与NIO**:对比传统的IO流和NIO(New IO)模型,理解通道(Channel)和缓冲区(Buffer)的概念。 12. **JDBC**:数据库连接,执行SQL语句,处理结果集,事务管理等数据库操作。 13. **Swing或JavaFX**:...

    李兴华java word版全部笔记

    同时,笔记也将涵盖Java NIO(New IO)框架,讲解通道、缓冲区和选择器等新特性,帮助读者提升IO操作的效率。 【网络编程】 Java提供了丰富的API支持网络编程,笔记会讲解Socket编程,包括TCP和UDP协议,以及...

    Java随堂笔记

    - **IO与NIO**:传统IO与非阻塞IO的区别和应用场景。 - **反射机制**:运行时动态获取类信息和调用方法。 - **注解(Annotation)**:自定义注解及其在编译、运行时的应用。 10. **Java EE基础** - **Servlet**...

    java学习笔记-老师的课程笔记

    4. NIO(New IO):介绍NIO的非阻塞特性,通道(Channel)和缓冲区(Buffer)的概念,以及选择器(Selector)的使用。 七、多线程 1. 线程的创建:通过Thread类和Runnable接口创建线程。 2. 线程同步:讲解...

    java笔记绝对干货

    - **缓冲流**:如何利用BufferedReader和BufferedWriter提高读写效率。 - **NIO**:讲解非阻塞I/O,包括Channel、Selector和Buffer的使用。 7. **网络编程** - **Socket通信**:建立客户端-服务器模型,实现基于...

    java学习笔记4

    笔记可能涵盖了File类的使用、流的概念(字符流与字节流)、缓冲区(BufferedInputStream/Writer)以及NIO的通道(Channels)和选择器(Selectors)。 4. **网络编程**:Java提供了Socket和ServerSocket类进行网络...

    java学习笔记之大鹏JAVA终级总结

    - NIO(New IO):非阻塞IO,选择器(Selector),通道(Channel)和缓冲区(Buffer)的概念。 6. **反射与注解** - 反射:通过Class对象动态操作类,包括实例化对象、获取和修改成员变量、调用方法。 - 注解:...

    Java所有课程超全学习笔记

    3.3 NIO(New IO):通道(Channel)、缓冲区(Buffer)、选择器(Selector),非阻塞I/O的优势。 四、多线程 4.1 线程创建:通过Thread类和实现Runnable接口两种方式创建线程。 4.2 线程同步:synchronized...

    Java学习超强笔记

    - NIO(New IO):非阻塞I/O,包括通道(Channel)、缓冲区(Buffer)和选择器(Selector)。 7. **多线程** - 线程的创建:通过Thread类或实现Runnable接口。 - 线程同步:synchronized关键字,wait()、notify...

    Java公司培训经典学习笔记

    - **NIO(New IO)**:介绍通道(Channel)、缓冲区(Buffer)、选择器(Selector)等核心概念。 - **文件操作**:文件的创建、删除、重命名,以及文件读写的相关API。 6. **多线程** - **线程创建**:通过...

    Java 客户端服务器程序 学习笔记

    `java.nio`包提供了选择器(Selector)、通道(Channel)和缓冲区(Buffer)等概念,允许程序在多个连接上进行非阻塞读写。 3. **HTTP客户端库**:除了低级的Socket API,Java还提供了许多库来处理HTTP协议,如...

    Java-Guide (Java核心知识笔记)

    **Java-Guide:Java核心知识笔记** 这是一份详尽的Java学习资源,旨在帮助Java开发者巩固和深化其核心知识。这份笔记包含了从基础到高级的各类Java编程概念,是学习和进阶Java技术的宝贵资料。Markdown格式使得内容...

    王者归来之良葛格java学习笔记JDK6课件和课本代码.rar

    - NIO(New IO)的介绍,包括通道(Channels)、缓冲区(Buffers)和选择器(Selectors)。 6. **多线程**: - 创建线程的两种方式:继承Thread类和实现Runnable接口。 - 线程同步机制,如synchronized关键字、...

    javase全套笔记

    10. **IO与NIO**:对比讲解传统IO和New IO(NIO)的区别,包括缓冲区、选择器和通道的使用。 11. **多线程**:介绍线程的创建方式(实现Runnable接口和继承Thread类)、线程同步机制(synchronized、wait/notify、...

    core java经典教程_整理笔记版

    笔记会讲解File类、InputStream和OutputStream家族、BufferedReader和Writer的使用,以及NIO的通道、缓冲区和选择器概念。 六、多线程 Java支持多线程编程,让程序能同时执行多个任务。笔记将阐述Thread类的使用,...

    韩顺平-循序渐进学Java零基础完整笔记

    - **NIO(非阻塞I/O)**:通道、缓冲区、选择器的概念,NIO在高并发场景中的优势。 9. **线程与并发** - **线程的创建**:通过继承Thread类和实现Runnable接口创建线程。 - **线程状态**:新建、就绪、运行、...

Global site tag (gtag.js) - Google Analytics