Java网络编程从入门到精通(31):非阻塞I/O简介

简介:
在网络应用中,一般可以采用同步 I/O (阻塞 I/O )和非阻塞 I/O 两种方式进行数据通讯。这两种方式并非互相排斥和互相取代。我们可以在平时的应用中单独采用其中一种通讯方式,也可以混合使用这两种通讯方式。在本文中就什么是非阻塞 I/O 以及为什么要使用这种通讯方式进行了介绍,在下一篇文章中给出了一个简单的例子来演示在网络应用中如何使用非阻塞 I/O 进行通讯。
一、 什么是非阻塞 I/O
我们可以将同步I/O 称为阻塞I/O ,非阻塞I/O 称为异步I/O 。在本书中采用了比较常用的叫法:同步I/O 和非阻塞I/O 。虽然它们的叫法不同,但含义是一样的。读者在阅读其他书时应注意这一点。
在讲解什么是非阻塞I/O 之前,首先应了解什么是同步I/O ,这里的同步指的是什么。同步这个概念在程序设计中主要是指代码按顺序执行的过程。如在Java 程序中的main 方法,如果不使用多线程,这个方法中的代码一定是从前往后按顺序执行的。这就叫做同步。如果使用了多线程,从宏观角度来看会有不同的代码段同时执行,这就叫做异步。在同步I/O 中的同步概念也类似,也就是说,在I/O 通讯过程中,只要是某一步的通讯未结束,就无法进行其他的通讯。那么这里的同步指的是什么呢?要回答这个问题之前,首先让我们先回忆一下,在网络通讯中有哪些地方可能会被阻塞。如果读者看了前面的章节就会知道答案。对于客户端来说,有两个地方可能会被阻塞:连接服务器(调用connect 方法时)和读写数据。而在服务端也有两个地方可能会被阻塞:等待客户端请求(调用accept 方法时)和读写数据(在一般情况下,写数据不会被阻塞,但如果网络环境比较差的时候,客户端和服务端的写数据操作也可能发生阻塞现象)。也就是说,可以设置超时时间的地方就可能被阻塞。而同步I/O 中的同步就是指除了以下两种情况外程序会一直处于等待状态:
1. 连接服务器、读写数据或等待客户端请求正常地执行。
2. 在等待超时时间后,抛出了超时异常。
在上面我们了解了什么是同步I/O 。而非阻塞I/O 和同步I/O 最明显的不同就是同步I/O 所有可能被阻塞的地址在非阻塞I/O 中都不会被阻塞。如在读取数据时,如果数据暂时无法被读取。那么在非阻塞I/O 中会立刻返回,以便程序可以执行其他的代码,然后系统会不断侦测这个未完成的读取操作,直到可以继续读数据时再来完成这个操作。
Java JDK1.4 及以后版本中提供了一套API 来专门操作非阻塞I/O ,我们可以在java.nio 包及其子包中找到相关的类和接口。由于这套API JDK 新提供的I/O API ,因此,也叫New I/O ,这就是包名nio 的由来。这套API 由三个主要的部分组成:缓冲区(Buffers) 、通道(Channels) 和非阻塞I/O 的核心类组成。这三部分的详细内容将在本章的后面介绍。
二、 为什么要使用非阻塞 I/O
在使用同步I/O 的网络应用中,如果要同时处理多个客户端请求,或是在客户端要同时和多个服务器进行通讯,就必须使用多线程来处理。也就是说,将每一个客户端请求分配给一个线程来单独处理。这样做虽然可以达到我们的要求,但同时又会带来另外一个问题。由于每创建一个线程,就要为这个线程分配一定的内存空间(也叫工作存储器),而且操作系统本身也对线程的总数有一定的限制。如果客户端的请求过多,服务端程序可能会因为不堪重负而拒绝客户端的请求,甚至服务器可能会因此而瘫痪。
当然,可以使用线程池(将在第三部分讲解)来缓解服务器的压力,但这并不能解决客户端因访问过于密集而造成的服务器拒绝响应的问题。虽然在服务端还有请求缓冲区作为保障,但这个缓冲区的大小是有限的(一般为50 ),如果客户端的请求数远超过这个数,客户端还是会收到拒绝服务的信息。
在这种情况下,使用非阻塞I/O 就可以解决这个问题。由于使用非阻塞I/O 的程序一般是单线程的(有时可能将使用非阻塞I/O 的程序段放到一个单独的线程里,而主线程负责处理用户的输入),因此,服务端接收的客户端请求数并不随着工作线程数的增加而增加。所以使用非阻塞I/O 模式就不会受到操作系统对线程总数的限制,也不会占用大量的服务器资源。
非阻塞I/O 虽然可以到达在处理大量客户端请求的同时,又不占用大量的服务器资源的目的。但这种通讯方式并不能完全取代同步I/O 。如非阻塞I/O 并不适合象FTP 服务器那样需要保持连接状态的应用(原因将在以后的章节中说明)。非阻塞I/O 一般应用在服务端比较多一些,因为客户端一般并不需要处理大量的连接(但某些应用除外,如象百度、Google Web Spider ,需要同时下载多个网页,这时就需要在客户端建立大量的连接来满足需求),而服务端程序一般需要接收并处理大量的客户端请求,因此,就需要使用多线程(使用同步I/O )或非阻塞I/O 来达到这个目的。如果某个服务端应用处理的客户端请求没那么多时,使用多线程和同步I/O 可能会更好一点,因为这种方式要比非阻塞I/O 方式更灵活。
在前面一直将非阻塞I/O 和网络应用放到一起讲。其实非阻塞I/O 并不等于网络。我们也可以将非阻塞I/O 应用到非网络的应用中,如文件复制。由于同步I/O 是基于字节流的,而非阻塞I/O 是基于缓冲区和通道的。因此,从理论上,所操作的文件越大,非阻塞I/O 的优势越能体现出来。而对于比较小的文件操作,这两种方式的效率差不多。根据实验得知,复制一个4G 左右的文件,一般情况下,非阻塞I/O 方式比同步I/O 方式快大约15% 左右。






 本文转自 androidguy 51CTO博客,原文链接: http://blog.51cto.com/androidguy/214343 ,如需转载请自行联系原作者
相关文章
|
9月前
|
JSON 移动开发 网络协议
Java网络编程:Socket通信与HTTP客户端
本文全面讲解Java网络编程,涵盖TCP与UDP协议区别、Socket编程、HTTP客户端开发及实战案例,助你掌握实时通信、文件传输、聊天应用等场景,附性能优化与面试高频问题解析。
|
7月前
|
JSON 网络协议 安全
【Java】(10)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
397 1
|
7月前
|
JSON 网络协议 安全
【Java基础】(1)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
372 1
|
10月前
|
人工智能 安全 Java
Go与Java泛型原理简介
本文介绍了Go与Java泛型的实现原理。Go通过单态化为不同类型生成函数副本,提升运行效率;而Java则采用类型擦除,将泛型转为Object类型处理,保持兼容性但牺牲部分类型安全。两种机制各有优劣,适用于不同场景。
557 24
|
存储 缓存 算法
JVM简介—1.Java内存区域
本文详细介绍了Java虚拟机运行时数据区的各个方面,包括其定义、类型(如程序计数器、Java虚拟机栈、本地方法栈、Java堆、方法区和直接内存)及其作用。文中还探讨了各版本内存区域的变化、直接内存的使用、从线程角度分析Java内存区域、堆与栈的区别、对象创建步骤、对象内存布局及访问定位,并通过实例说明了常见内存溢出问题的原因和表现形式。这些内容帮助开发者深入理解Java内存管理机制,优化应用程序性能并解决潜在的内存问题。
740 29
JVM简介—1.Java内存区域
|
10月前
|
人工智能 Java
java中static关键字简介
`static`关键字用于修饰类的成员变量和方法,使其属于类而非对象。静态成员可通过类名直接访问,无需实例化对象。静态方法只能访问静态成员,不能直接访问非静态成员或使用`this`关键字。此外,静态代码块在类首次加载时执行且仅执行一次,适用于初始化操作。
257 0
|
存储 网络协议 安全
Java网络编程,多线程,IO流综合小项目一一ChatBoxes
**项目介绍**:本项目实现了一个基于TCP协议的C/S架构控制台聊天室,支持局域网内多客户端同时聊天。用户需注册并登录,用户名唯一,密码格式为字母开头加纯数字。登录后可实时聊天,服务端负责验证用户信息并转发消息。 **项目亮点**: - **C/S架构**:客户端与服务端通过TCP连接通信。 - **多线程**:采用多线程处理多个客户端的并发请求,确保实时交互。 - **IO流**:使用BufferedReader和BufferedWriter进行数据传输,确保高效稳定的通信。 - **线程安全**:通过同步代码块和锁机制保证共享数据的安全性。
624 23
|
Java Linux API
课时3:Java简介(Java主要特点)
本文介绍了Java的主要特点及其运行机制。Java结合了编译型和解释型语言的优点,通过Java虚拟机(JVM)实现跨平台移植,简化了不同操作系统间的开发流程。Java的特点包括可移植性、简单易用、支持多线程编程、自动垃圾收集和面向对象编程。随着硬件技术的发展,Java的性能问题已大大改善,成为行业标准之一,广泛应用于各种商用平台开发。
378 1
|
安全 网络协议 Java
Java网络编程封装
Java网络编程封装原理旨在隐藏底层通信细节,提供简洁、安全的高层接口。通过简化开发、提高安全性和增强可维护性,封装使开发者能更高效地进行网络应用开发。常见的封装层次包括套接字层(如Socket和ServerSocket类),以及更高层次的HTTP请求封装(如RestTemplate)。示例代码展示了如何使用RestTemplate简化HTTP请求的发送与处理,确保代码清晰易维护。
|
存储 JavaScript Java
Java 中的 String Pool 简介
本文介绍了 Java 中 String 对象及其存储机制 String Pool 的基本概念,包括字符串引用、构造方法中的内存分配、字符串文字与对象的区别、手工引用、垃圾清理、性能优化,以及 Java 9 中的压缩字符串特性。文章详细解析了 String 对象的初始化、内存使用及优化方法,帮助开发者更好地理解和使用 Java 中的字符串。
273 2
Java 中的 String Pool 简介