前面讲到:Java IO编程全解(一)——Java的I/O演进之路

  网络编程的基本模型是Client/Server模型,也就是两个进程之间进行相互通信,其中服务端提供位置信息(绑定的IP地址和监听端口),客户端通过连接操作向服务端监听的地址发起连接请求,通过三次握手建立连接,如果连接建立成功,双方就可以通过网络套接字(Socket)进行通信。

  在基于传统同步阻塞模型开发中,ServerSocket负责绑定IP地址,启动监听端口;Socket负责发起连接操作。连接成功之后,双方通过输入和输出流进行同步阻塞式通信。

  以经典的时间服务器(TimeServer)为例,通过代码分析来回顾和熟悉下BIO编程。

1. BIO通信模型图

  如下图BIO的服务端通信模型:采用BIO通信模型的服务端,通常由一个独立的Acceptor线程负责监听客户端的连接,它接收到客户端连接请求之后为每个客户端创建一个新的线程进行链路处理,处理完成之后,通过输出流返回应答给客户端,线程销毁。这就是典型的一请求一应答通信模型。

图1  同步阻塞I/O服务端通信模型(一客户端一线程)

  该模型最大的问题就是缺乏弹性伸缩能力,当客户端并发访问量增加后,服务端的线程个数和客户端并发访问数呈1:1 的正比关系,犹豫线程是Java虚拟机非常宝贵的系统资源,当线程数膨胀之后,系统的性能将急剧下降,随着并发访问量的继续增大,系统会发生线程堆栈溢出、创建新线程失败等问题,并最终导致进程宕机或者僵死,不能对外提供服务。

2. 同步阻塞式I/O创建的TimeServer

package joanna.yan.bio;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket; public class TimeServer { public static void main(String[] args) {
int port=9090;
if(args!=null&&args.length>0){
try {
port=Integer.valueOf(args[0]);
} catch (Exception e) {
// 采用默认值
}
} ServerSocket server=null; try {
server=new ServerSocket(port);
System.out.println("The time server is start in port:"+port);
Socket socket=null;
while(true){//通过一个无限循环来监听客户端的连接
socket=server.accept();//如果没有客户端接入,则主线程阻塞在ServerSocket的accept操作上。
new Thread(new TimeServerHandler(socket)).start();
}
} catch (IOException e) {
e.printStackTrace();
}finally{
if(server!=null){
System.out.println("The time server close");
try {
server.close();
server=null;
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
package joanna.yan.bio;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Date; public class TimeServerHandler implements Runnable{
private Socket socket; public TimeServerHandler(Socket socket) {
this.socket = socket;
} @Override
public void run() {
BufferedReader in=null;
PrintWriter out=null; try {
in=new BufferedReader(new InputStreamReader(this.socket.getInputStream()));
out=new PrintWriter(this.socket.getOutputStream(), true);
String currentTime=null;
String body=null;
while(true){
body=in.readLine();
if(body==null){
break;
}
System.out.println("The time server receive order:"+body);
//如果请求消息为查询时间的指令"QUERY TIME ORDER"则获取当前最新的系统时间。
currentTime="QUERY TIME ORDER".equalsIgnoreCase(body) ?
new Date(System.currentTimeMillis()).toString() : "BAD ORDER";
out.println(currentTime);
}
} catch (IOException e) {
e.printStackTrace();
}finally{
if(in!=null){
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(out!=null){
out.close();
out=null;
}
if(this.socket!=null){
try {
this.socket.close();
this.socket=null;
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}

3. 同步阻塞式I/O创建的TimeClient

  客户端通过Socket创建,发送查询时间服务器的“QUERY TIME ORDER”指令,然后读取服务端的响应并将结果打印出来,随后关闭连接,释放资源,程序退出执行。

package joanna.yan.bio;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException; public class TimeClient {
public static void main(String[] args) {
int port=9090;
if(args!=null&&args.length>0){
try {
port=Integer.valueOf(args[0]);
} catch (Exception e) {
// 采用默认值
}
} Socket socket=null;
BufferedReader in=null;
PrintWriter out=null; try {
socket=new Socket("127.0.0.1",port);
in=new BufferedReader(new InputStreamReader(socket.getInputStream()));
out=new PrintWriter(socket.getOutputStream(),true);
out.println("QUERY TIME ORDER");
System.out.println("Send order 2 server succeed.");
String resp=in.readLine();
System.out.println("Now is:"+resp); } catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
if(out!=null){
out.close();
out=null;
}
if(in!=null){
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
in=null;
}
if(socket!=null){
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
socket=null;
}
}
}
}

  我们发现,BIO主要的问题在于每当有一个新的客户端请求接入时,服务端必须创建一个新的线程处理新接入的客户端链路,一个线程只能吃力一个客户端连接。在高性能服务器应用领域,往往需要面向成千上万个客户端的并发连接,这种模型显然无法满足高性能、高并发接入的场景。

  为了改进一线程一连接模型,后来又演进出了一种通过线程池或者消息队列实现1个或者多个线程处理N个客户端的模型,由于它的底层通信机制依然使用同步阻塞I/O,所以被称为“伪异步”。后面我们将通过对伪异步代码的分析,看看伪异步能否满足我们对高性能、高并发接入的诉求。

Java IO编程全解(三)——伪异步IO编程

如果此文对您有帮助,微信打赏我一下吧~

Java IO编程全解(二)——传统的BIO编程的更多相关文章

  1. Java IO编程全解(四)——NIO编程

    转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/7793964.html 前面讲到:Java IO编程全解(三)——伪异步IO编程 NIO,即New I/O,这 ...

  2. Java IO编程全解(五)——AIO编程

    转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/7794151.html 前面讲到:Java IO编程全解(四)--NIO编程 NIO2.0引入了新的异步通道的 ...

  3. Java io流详解二

    原文地址https://www.cnblogs.com/xll1025/p/6418766.html 一.IO流概述 概述: IO流简单来说就是Input和Output流,IO流主要是用来处理设备之间 ...

  4. Java IO编程全解(三)——伪异步IO编程

    转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/7723174.html 前面讲到:Java IO编程全解(二)--传统的BIO编程 为了解决同步阻塞I/O面临 ...

  5. Java IO编程全解(六)——4种I/O的对比与选型

    转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/7804185.html 前面讲到:Java IO编程全解(五)--AIO编程 为了防止由于对一些技术概念和术语 ...

  6. Java IO编程全解(一)——Java的I/O演进之路

    转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/7419117.html JDK1.4之前的早期版本,Java对I/O的支持并不完善,开发人员在开发高性能I/O ...

  7. JAVA IO 类库详解

    JAVA IO类库详解 一.InputStream类 1.表示字节输入流的所有类的超类,是一个抽象类. 2.类的方法 方法 参数 功能详述 InputStream 构造方法 available 如果用 ...

  8. Java IO流详解(二)——File类

    在上一章博客中简单的介绍了Java IO流的一些特征.也就是对文件的输入输出,既然至始至终都离不开文件,所以Java IO流的使用得从File这个类讲起. File类的描述:File类是文件和目录路径 ...

  9. netty学习(二)--传统的bio编程

    网络编程的基本模型是Client/Server模型.也就是两个进程之间进行相互通信,当中服务端提供位置信息( 绑定ip地址和监听port),client通过连接操作向服务端监听的地址发送连接请求,通过 ...

随机推荐

  1. cocos2dx JS layuot纯代码实现背景颜色渐变

    // view._partyBtnClassify.setBackGroundColorType(ccui.Layout.BG_COLOR_GRADIENT);// view._partyBtnCla ...

  2. mysql----------mysql5.7.11导入sql文件时报错This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its declaration and binary logging is enabled

    1.导入sql文件出现如下错误. [Err] 1418 - This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in ...

  3. arch----------arch下的一些命令,亲测

    1.taoyanghao 不在 sudoers 文件中.此事将被报告. 这个是使用sudo以后报出的错误提示,sudo确定已经安装了. 解决方案:编辑/etc/sudoers文件.找到这一 行:&qu ...

  4. 024-母版页MasterPage

    网站的布局通常是统一的,上面是Logo.菜单条.下面是公司地址.版权声明等.如果每个页面都重复做这些功能的话:重复性劳动.一旦修改那么每个页面都要修改..Net中一般用母版(MasterPage)技术 ...

  5. CentOS 7 使用SVN+Apache搭建版本控制服务器

    svn简介 Subversion是一个免费/开源的版本控制系统, Subversion 可以跨越时间地对文件和目录, 以及它们的修改进行管理. 这就允许你恢复 数据的旧版本, 或检查数据的修改历史. ...

  6. 杨韬的Markdown自定义CSS样式

    效果 样例 杨韬的Python/Jupyter学习笔记 Markdown例子链接页面 代码 /*主标题*/ #cb_post_title_url{ font-size: 31px; } /*h1,h2 ...

  7. Android开发中使用Intent跳转到系统应用中的拨号界面、联系人界面、短信界面

    现在开发中的功能需要直接跳转到拨号.联系人.短信界面等等,查找了很多资料,自己整理了一下. 首先,我们先看拨号界面,代码如下: Intent intent =new Intent(); intent. ...

  8. 怎么让table中的<td>内容向上对齐

    <td valign="top"></td>

  9. Python 协程并发爬虫网页

    简单爬虫实例: 功能:通过urllib.request实现网站爬虫,捕获网站内容. from urllib import request def f(url): print("GET:%s& ...

  10. docker安装openwrt镜像(不完美案例)

    镜像从http://downloads.openwrt.org/releases下载 注意选择generic-rootfs.tar.gz这种类型的镜像 使用docker import导入镜像,导入后可 ...