在上一节我们介绍了四种IO相关编程的各个特点,并通过代码进行复习了传统的网络编程代码,伪异步主要是引用了线程池,对BIO中服务端进行了相应的改造优化,线程池的引入,使得我们在应对大量客户端请求的时候不会导致虚拟机可贵的线程资源耗尽而宕机,在本小节,我们来看看伪异步代码。多注意引入的线程池代码。

  伪异步IO模型图如下():

  当有新的客户端请求到来时,将socket封装成一个Task(该任务实现了Runnable接口),投递到线程池中进行处理。而线程池始终维护一个消息队列和N个活跃着的线程,这些线程被分配,对消息队列中的Task进行处理。由于线程池可以设置消息队列的大小和线程的数量,因此不论多少个客户端请求都不会导致资源耗尽和宕机。

下面附上刚改造完成的代码:为了便于理解,在代码的最后本博主添加上关于线程池运行的原理的基本知识,以便于理解伪I/O的代码。

改造后的服务端代码:

 package com.example.biodemo;

 import java.io.*;
import java.net.ServerSocket;
import java.net.Socket; public class TimeServer {
public static void main(String[] args) throws IOException {
int port = 8090;
if (args != null && args.length > 0) {
try {
port = Integer.valueOf(args[0]);
} catch (NumberFormatException e) {
port = 8090;
}
}
ServerSocket server = null;
try {
server = new ServerSocket(port);
System.out.println("the timeServer is start in port :" + port);
Socket socket = null;
// 引入线程池start
TimeServerHandlerExecutePool singleExecutor = new TimeServerHandlerExecutePool(50,10000);
while (true) {
socket = server.accept();
// 替换BIO中new Thread(new TimeServerHandler(socket)).start();为下一行代码
singleExecutor.execute(new TimeServerHandler(socket));
// 引入线程池end }
} finally {
if (server != null) {
System.out.println("the time server close");
server.close();
server = null;
}
} }
}

服务端引用的线程池,自定义的线程池代码

 package com.example.biodemo;

 import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit; //自定义的线程池
public class TimeServerHandlerExecutePool {
private ExecutorService executor;
// 线程池构造函数,创建线程池
public TimeServerHandlerExecutePool(int maxPoolSize,int queueSize){
// new ThreadPoolExecutor(线程池基本大小,线程池最大数量,存活时间,时间单位,消息队列)
executor = new ThreadPoolExecutor(Runtime.getRuntime().availableProcessors(),maxPoolSize,120L,
TimeUnit.SECONDS,new ArrayBlockingQueue<Runnable>(queueSize)); }
// 该方法会不断从消息队列中获取任务执行
public void execute(java.lang.Runnable task){
executor.execute(task);
} }

下面是和上一小节一样的 TimeServerHandler和客户端代码

package com.example.biodemo;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket; 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(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);
currentTime = "QUERY TIME ORDER".equalsIgnoreCase(body) ? new java.util.Date(System.currentTimeMillis()).toString() : "bad order";
out.println(currentTime); }
} catch (Exception e) {
if (in != null) {
try {
in.close();
} catch (IOException el) {
el.printStackTrace();
}
}
if (out != null) {
out.close();
out = null;
}
if (this.socket != null) {
try {
this.socket.close();
} catch (IOException e1) {
e1.printStackTrace();
}
this.socket = null;
}
}
}
}

客户端代码

 package com.example.biodemo;

 import java.io.*;
import java.net.Socket; public class TimeClient {
public static void main(String[] args) {
int port = 8090;
if (args != null && args.length > 0) {
try {
port = Integer.valueOf(args[0]);
} catch (NumberFormatException ne) {
port = 8090;
}
}
Socket socket = null;
BufferedReader in = null;
PrintWriter out = null;
try {
socket = new Socket("127.0.0.1", port);
System.out.println(socket.getInputStream());
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 (IOException e1) { } finally {
if (out != null) {
out.close();
out = null;
} if (in != null) {
try {
in.close();
} catch (IOException e2) {
e2.printStackTrace();
}
in = null;
if (socket != null) {
try {
socket.close();
} catch (IOException e3) {
e3.printStackTrace();
} }
socket = null;
} }
}
}

其运行与小节一结果一致。

下面为了便于理解添加的线程池相关的代码部分,附上一些线程池运行的原理等相关知识。

  当提交一个新任务到线程池时,线程池的处理流程如上图所示,具体为:

  1、判断线程池里的核心线程是否都在执行任务,如果不是(核心线程空闲或者还有核心线程没有被创建)则创建一个新的工作线程来执行任务。如果核心线程都在执行任务,则进入下个流程。

  2、线程池判断工作队列是否已满,如果工作队列没有满,则将新提交的任务存储在这个工作队列里。如果工作队列满了,则进入下个流程。
  3、判断线程池里的线程是否都处于工作状态,如果没有,则创建一个新的工作线程来执行任务。如果已经满了,则交给饱和策略来处理这个任务。

  上面这个图是ThreadPoolExecutor 执行 executor的示意图。ThreadPoolExecutor 执行 executor方法分为下面4种情况:

  1、如果当前运行的线程少于corePoolSize,则创建新线程来执行任务(注意,执行这一步骤需要获取全局锁)。

  2、如果运行的线程等于或者多于corePoolSize,则任务添加到BlockingQueue。

  3、如果无法将任务加入到BlockingQueue(队列已满),则创建新的线程来处理任务(注意,执行这一步骤需要获取全局锁)。

  4、如果创建新线程将使当前运行的线程超出maximumPoolSize,任务将被拒绝,并调用RejectedExecutionHandler.rejectedExecution()方法。

如上,相信大家能够更好的理解伪异步I/O与BIO的不同了。

netty权威指南学习笔记一——NIO入门(2)伪异步IO的更多相关文章

  1. netty权威指南学习笔记一——NIO入门(1)BIO

    公司的一些项目采用了netty框架,为了加速适应公司开发,本博主认真学习netty框架,前一段时间主要看了看书,发现编程这东西,不上手还是觉得差点什么,于是为了加深理解,深入学习,本博主还是决定多动手 ...

  2. netty权威指南学习笔记一——NIO入门(3)NIO

    经过前面的铺垫,在这一节我们进入NIO编程,NIO弥补了原来同步阻塞IO的不足,他提供了高速的.面向块的I/O,NIO中加入的Buffer缓冲区,体现了与原I/O的一个重要区别.在面向流的I/O中,可 ...

  3. netty权威指南学习笔记一——NIO入门(4)AIO

    NIO2.0引入了新的异步通道的概念,并提供了异步文件通道和异步套接字通道的实现.异步通道提供以下两种方式获取操作结果. 1.通过java.util.concurrent.Future 类来表示异步操 ...

  4. netty权威指南学习笔记二——netty入门应用

    经过了前面的NIO基础知识准备,我们已经对NIO有了较大了解,现在就进入netty的实际应用中来看看吧.重点体会整个过程. 按照权威指南写程序的过程中,发现一些问题:当我们在定义handler继承Ch ...

  5. netty权威指南学习笔记六——编解码技术之MessagePack

    编解码技术主要应用在网络传输中,将对象比如BOJO进行编解码以利于网络中进行传输.平常我们也会将编解码说成是序列化/反序列化 定义:当进行远程跨进程服务调用时,需要把被传输的java对象编码为字节数组 ...

  6. netty权威指南学习笔记八——编解码技术之JBoss Marshalling

    JBoss Marshalling 是一个java序列化包,对JDK默认的序列化框架进行了优化,但又保持跟java.io.Serializable接口的兼容,同时增加了一些可调参数和附加特性,这些参数 ...

  7. netty权威指南学习笔记七——编解码技术之GoogleProtobuf

    首先我们来看一下protobuf的优点: 谷歌长期使用成熟度高: 跨语言支持多种语言如:C++,java,Python: 编码后消息更小,更利于存储传输: 编解码性能高: 支持不同协议版本的兼容性: ...

  8. netty权威指南学习笔记五——分隔符和定长解码器的应用

    TCP以流的方式进行数据传输,上层应用协议为了对消息进行区分,通常采用以下4中方式: 消息长度固定,累计读取到长度综合为定长LEN的报文后,就认为读取到了一个完整的消息,将计数器置位,重新开始读取下一 ...

  9. netty权威指南学习笔记三——TCP粘包/拆包之粘包现象

    TCP是个流协议,流没有一定界限.TCP底层不了解业务,他会根据TCP缓冲区的实际情况进行包划分,在业务上,一个业务完整的包,可能会被TCP底层拆分为多个包进行发送,也可能多个小包组合成一个大的数据包 ...

随机推荐

  1. sklearn中调用集成学习算法

    1.集成学习是指对于同一个基础数据集使用不同的机器学习算法进行训练,最后结合不同的算法给出的意见进行决策,这个方法兼顾了许多算法的"意见",比较全面,因此在机器学习领域也使用地非常 ...

  2. 对C/C++指针问题的彻底理解(复习1)

    理解第一篇 上完大学不是我吹(仅仅说我认识的本学校本专业)估计很明白指针的人绝对不超过百分之50,学校low不low另说(因为问他们讲得断断续续的自己也说不明白,只知道那样可以用) 学链表树形结构.. ...

  3. swift简介(东拼西凑,看看就的了)

    OpenStack Object Storage(Swift)架构.原理及特性 https://yq.aliyun.com/articles/50262 原文 摘要: 简介 OpenStack Obj ...

  4. 如何实现从登录接口响应数据中提取JSESSIONID

    通过调用接口,对服务器产生压力,单接口测试,会遇到响应数据返回的是要求先登录.这是我们在调用这个接口时,根据业务逻辑,必须先登录才可以,比如:提交订单,那么就需要先获取用户登录的JSESSIONID, ...

  5. java学习-抽象类和接口-抽象类初体验

    今天在老师的带领下学习了抽象类(abstract)和接口(interface),总结一下今天所学的知识. 抽象类是在学习到接口时之前讲解,作为学习接口的铺垫. 下面是我今天写的例题. 一个Shape( ...

  6. linux vsftpd 550 create directory operation failed解决方法

    今天配置好了vsftp, 但登陆后,除了浏览,什么也干不了.(如新建文件/文件夹, 删除文件, 重命名等都不可操作) 都是弹出 "550 create directory operation ...

  7. Android之系统自带的文字外观设置及实际显示效果图

     android:textAppearance xml布局里面设置文字的外观: 如“android:textAppearance=“?android:attr/textAppearanceLargeI ...

  8. modelsim10.4环境变量的设置

    在用户变量中设置,注意路径还要包括license.txt 点击高级属性设置 点击环境变量 在用户变量一件名为:MGLS_LICENSE_FILE的变量 点击确定

  9. JavaScript函数用法

    本文我们来学习下js函数的一些用法. 上图的要点为: 1.函数具有属性,如foo.length和foo.name. 2.arguments是类数组,arguments.length为实参的数目. 3. ...

  10. Python递归函数如何写?正确的Python递归函数用法!

    在函数内部,可以调用其他函数.如果一个函数在内部调用自身本身,这个函数就是递归函数.一.举个例子,我们来计算阶乘n! = 1 x 2 x 3 x … x n,用函数fact(n)表示,可以看出:fac ...