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
软件包提供了新的抽象。具体地说,就是 Channel
和Selector
类。
二、 缓冲区
(一)缓冲区操作
缓冲区,以及缓冲区如何工作,是所有 I/O 的基础。所谓“输入/输出”讲的无非就是把数据移进或移出缓冲区。
进程执行 I/O 操作,归结起来,也就是向操作系统发出请求,让它要么把缓冲区里的数据排干(写),要么用数据把缓冲区填满(读)。进程使用这一机制处理所有数据进出操作。操作系统内部处理这一任务的机制,其复杂程度可能超乎想像,但就概念而言,却非常直白易懂。图 1-1 简单描述了数据从外部磁盘向运行中的进程的内存区域移动的过程。进程使用read()
系统调用,要求其缓冲区被填满。内核随即向磁盘控制硬件发出命令,要求其从磁盘读取数据。磁盘控制器把数据直接写入内核内存缓冲区,这一步通过 DMA 完成,无需主CPU协助。一旦磁盘控制器把缓冲区装满,内核即把数据从内核空间的临时缓冲区拷贝到进程执行read()
调用时指定的缓冲区。
JVM 就是常规进程,驻守于用户空间。用最重要的是,所有 I/O 都直接或间接通过内核空间。当进程请求 I/O 操作的时候,它执行一个系统调用(有时称为陷阱)将控制权移交给内核。C/C++程序员所熟知的底层函数open()
、read()
、write()
和close()
要做的无非就是建立和执行适当的系统调用。当内核以这种方式被调用,它随即采取任何必要步骤,找到进程所需数据,并把数据传送到用户空间内的指定缓冲区。内核试图对数据进行高速缓存或预读取,因此进程所需数据可能已经在内核空间里了。如果是这样,该数据只需简单地拷贝出来即可。如果数据不在内核空间,则进程被挂起,内核着手把数据读进内存。
为什么不直接让磁盘控制器把数据送到用户空间的缓冲区呢?这样做有几个问题。首先,硬件通常不能直接访问用户空间。其次,像磁盘这样基于块存储的硬件设备操作的是固定大小的数据块,而用户进程请求的可能是任意大小的或非对齐的数据块。在数据往来于用户空间与存储设备的过程中,内核负责数据的分解、再组合工作,因此充当着中间人的角色。
(二) 发散/汇聚
许多操作系统能把组装/分解过程进行得更加高效。根据发散/汇聚的概念,进程只需一个系统调用,就能把一连串缓冲区地址传递给操作系统。然后,内核就可以顺序填充或排干多个缓冲区,读的时候就把数据发散到多个用户空间缓冲区,写的时候再从多个缓冲区把数据汇聚起来(图1-2)。
这样用户进程就不必多次执行系统调用(那样做可能代价不菲),内核也可以优化数据的处理过程,因为它已掌握待传输数据的全部信息。如果系统配有多个 CPU,甚至可以同时填充或排干多个缓冲区。
三、 流I/O
并非所有 I/O 都像前几节讲的是面向块的,也有流 I/O,其原理模仿了通道。I/O 字节流必须顺序存取,常见的例子有TTY(控制台)设备、打印机端口和网络连接。
流的传输一般(也不必然如此)比块设备慢,经常用于间歇性输入。多数操作系统允许把流置于非块模式,这样,进程可以查看流上是否有输入,即便当时没有也不影响它干别的。这样一种能力使得进程可以在有输入的时候进行处理,输入流闲置的时候执行其他功能。
比非块模式再进一步,就是就绪性选择。就绪性选择与非块模式类似(常常就是建立在非块模式之上),但是把查看流是否就绪的任务交给了操作系统。操作系统受命查看一系列流,并提醒进程哪些流已经就绪。这样,仅仅凭借操作系统返回的就绪信息,进程就可以使用相同代码和单一线程,实现多活动流的多路传输。这一技术广泛用于网络服务器领域,用来处理数量庞大的网络连接。就绪性选择在大容量缩放方面是必不可少的。
四、Socket通道
DatagramChannel
和SocketChannel
实现定义读和写功能的接口而ServerSocketChannel
不实现。ServerSocketChannel
负责监听传入的连接和创建新的SocketChannel
对象,它本身从不传输数据。
socket和socket通道之间的关系。之前的章节中有写道,通道是一个连接I/O服务导管并提供与该服务交互的方法。就某个socket而言,它不会再次实现与之对应的socket通道类中的socket协议 API,而java.net
中已经存在的socket通道都可以被大多数协议操作重复使用。
全部socket通道类(DatagramChannel
、SocketChannel
和ServerSocketChannel
)在被实例化时都会创建一个对等socket对象。这些是我们所熟悉的来自java.net
的类(Socket
、ServerSocket
和DatagramSocket
),它们已经被更新以识别通道。对等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流是Java平台中用于处理输入输出的重要组成部分,它为数据在不同设备间传输提供了高效、灵活的方式。本文将深入探讨Java IO流的基本概念、分类、使用方法以及相关工具,帮助你更好地理解和掌握这一核心技能。...
这些可能涵盖了高级IO特性,如NIO(New IO,Java 1.4引入)和NIO.2(Java 7引入),包括选择器(Selector)、通道(Channel)、缓冲区(Buffer)和文件属性的处理。还有可能涉及网络IO,如Socket和ServerSocket的...
NIO包括通道(Channels)、缓冲区(Buffers)和选择器(Selectors),通过选择器,一个线程可以同时监控多个通道的事件,提高效率。文件“2013-12-26-java-newIO.txt”和“2013-12-27-java-newIO续.txt”应该会详细...
- **JAVA NIO**: 提供了基于通道(Channel)和缓冲区(Buffer)的新的IO处理方式,提高了IO操作的性能和灵活性。 - **NIO的缓冲区**: 用于存储不同数据类型的临时容器,包括ByteBuffer、CharBuffer等。 - **NIO的非...
11. **IO流与NIO**:对比传统的IO流和NIO(New IO)模型,理解通道(Channel)和缓冲区(Buffer)的概念。 12. **JDBC**:数据库连接,执行SQL语句,处理结果集,事务管理等数据库操作。 13. **Swing或JavaFX**:...
同时,笔记也将涵盖Java NIO(New IO)框架,讲解通道、缓冲区和选择器等新特性,帮助读者提升IO操作的效率。 【网络编程】 Java提供了丰富的API支持网络编程,笔记会讲解Socket编程,包括TCP和UDP协议,以及...
- **IO与NIO**:传统IO与非阻塞IO的区别和应用场景。 - **反射机制**:运行时动态获取类信息和调用方法。 - **注解(Annotation)**:自定义注解及其在编译、运行时的应用。 10. **Java EE基础** - **Servlet**...
4. NIO(New IO):介绍NIO的非阻塞特性,通道(Channel)和缓冲区(Buffer)的概念,以及选择器(Selector)的使用。 七、多线程 1. 线程的创建:通过Thread类和Runnable接口创建线程。 2. 线程同步:讲解...
- **缓冲流**:如何利用BufferedReader和BufferedWriter提高读写效率。 - **NIO**:讲解非阻塞I/O,包括Channel、Selector和Buffer的使用。 7. **网络编程** - **Socket通信**:建立客户端-服务器模型,实现基于...
笔记可能涵盖了File类的使用、流的概念(字符流与字节流)、缓冲区(BufferedInputStream/Writer)以及NIO的通道(Channels)和选择器(Selectors)。 4. **网络编程**:Java提供了Socket和ServerSocket类进行网络...
- NIO(New IO):非阻塞IO,选择器(Selector),通道(Channel)和缓冲区(Buffer)的概念。 6. **反射与注解** - 反射:通过Class对象动态操作类,包括实例化对象、获取和修改成员变量、调用方法。 - 注解:...
3.3 NIO(New IO):通道(Channel)、缓冲区(Buffer)、选择器(Selector),非阻塞I/O的优势。 四、多线程 4.1 线程创建:通过Thread类和实现Runnable接口两种方式创建线程。 4.2 线程同步:synchronized...
- NIO(New IO):非阻塞I/O,包括通道(Channel)、缓冲区(Buffer)和选择器(Selector)。 7. **多线程** - 线程的创建:通过Thread类或实现Runnable接口。 - 线程同步:synchronized关键字,wait()、notify...
- **NIO(New IO)**:介绍通道(Channel)、缓冲区(Buffer)、选择器(Selector)等核心概念。 - **文件操作**:文件的创建、删除、重命名,以及文件读写的相关API。 6. **多线程** - **线程创建**:通过...
`java.nio`包提供了选择器(Selector)、通道(Channel)和缓冲区(Buffer)等概念,允许程序在多个连接上进行非阻塞读写。 3. **HTTP客户端库**:除了低级的Socket API,Java还提供了许多库来处理HTTP协议,如...
**Java-Guide:Java核心知识笔记** 这是一份详尽的Java学习资源,旨在帮助Java开发者巩固和深化其核心知识。这份笔记包含了从基础到高级的各类Java编程概念,是学习和进阶Java技术的宝贵资料。Markdown格式使得内容...
- NIO(New IO)的介绍,包括通道(Channels)、缓冲区(Buffers)和选择器(Selectors)。 6. **多线程**: - 创建线程的两种方式:继承Thread类和实现Runnable接口。 - 线程同步机制,如synchronized关键字、...
10. **IO与NIO**:对比讲解传统IO和New IO(NIO)的区别,包括缓冲区、选择器和通道的使用。 11. **多线程**:介绍线程的创建方式(实现Runnable接口和继承Thread类)、线程同步机制(synchronized、wait/notify、...
笔记会讲解File类、InputStream和OutputStream家族、BufferedReader和Writer的使用,以及NIO的通道、缓冲区和选择器概念。 六、多线程 Java支持多线程编程,让程序能同时执行多个任务。笔记将阐述Thread类的使用,...
- **NIO(非阻塞I/O)**:通道、缓冲区、选择器的概念,NIO在高并发场景中的优势。 9. **线程与并发** - **线程的创建**:通过继承Thread类和实现Runnable接口创建线程。 - **线程状态**:新建、就绪、运行、...