Java的I/O演进历史
1.jdk1.4之前使用同步阻塞,也就是BIO。像0几年QQ,那么大并发量,java开发的话是不可能做到高并发的,那时采用C或者C++, 因为可以直接操作系统提供的异步IO,AIO
2.jdk1.4推出NIO,支持非阻塞IO,jdk1.7升级,推出NIO2.0,提供AIO的功能,支持文件和网络套接字的异步IO
使用jdk自带的Bio编写一个同一时间服务
一个客户端请求过来服务返回对应的时间戳,每个请求都分一个线程
Step1:新建java项目
选取模板
Step2:创建Server服务端
public class BioServer {
//定义服务端端口号
private static final int PORT = 8080;
public static void main(String[] args) throws IOException {
//建立Socket连接
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();
//传入Hander
new Thread(new TimeServerHander(socket)).start();
}
}catch (Exception e){
e.printStackTrace();
}finally {
if(server != null){
System.out.println("the time server close");
server.close();
}
}
}
}
Step3:处理逻辑
public class TimeServerHander implements Runnable {
//接收socket
private Socket socket;
public TimeServerHander(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 body = null;
while ((body = in.readLine()) != null && body.length()!=0){
System.out.println("the time server receive msg:"+body);
//输出
out.println(new Date().toString());
//out.flush(); 上面写入了true 自动刷新
}
}catch (Exception e){
e.printStackTrace();
}finally {
//关闭
if(in != null){
try {
in.close();
}catch (Exception e){
e.printStackTrace();
}
}
if(out != null){
try {
out.close();
}catch (Exception e){
e.printStackTrace();
}
}
if(this.socket != null){
try {
this.socket.close();
}catch (Exception e){
e.printStackTrace();
}
}
}
}
}
Step4:创建客户端
public class BioClient {
//定义服务端口号
private static final int PORT= 8080;
private static final String HOST = "127.0.0.1";
public static void main(String [] args){
Socket socket = null;
//获取服务端返回的值
BufferedReader in = null;
//发送数据给服务端
PrintWriter out = null;
try {
socket = new Socket(HOST,PORT);
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
out = new PrintWriter(socket.getOutputStream(),true);
out.println("向服务端发送的消息:我是客户端");
String resp = in.readLine();
System.out.println("接收服务端返回的消息:当前服务器时间是:"+resp);
}catch (Exception e){
}finally {
if(in != null){
try {
in.close();
}catch (Exception e){
e.printStackTrace();
}
}
if(out != null){
try {
out.close();
}catch (Exception e){
e.printStackTrace();
}
}
if(socket != null){
try {
socket.close();
}catch (Exception e){
e.printStackTrace();
}
}
}
}
}
Step5:测试
先启动server端,再启动client端。server会一直启动等待连接,client向服务端发消息,server返回当前时间戳
BIO编写Client/server通信优缺点分析
BIO优缺点,为什么不能高并发情况下性能弱?
优点:
模型简单、编码简单(请求,分发 BIO模型非常简单 )
缺点:
性能瓶颈 每个请求都分配一个线程 如果一万个请求过来 分配一万个线程,电脑CPU处理线程多的时候需要上下文切换,损耗很大,如果这个线程处理还需要读数据库,编解码,这个线程就会一直耗着(请求书和线程数属于N:N关系)高并发情况下 CPU切换线程上下文损耗大
缺点的存在:JDK旧版本是没有NIO的 都是BIO通信
案例:Tomcat 7 之前都是使用BIO通信 7之后使用NIO(一个线程去维护多个操作 或者是线程池)
改进:伪NIO(也就是使用线程池去做 请求会在等待队列里)
文切换,损耗很大,如果这个线程处理还需要读数据库,编解码,这个线程就会一直耗着(请求书和线程数属于N:N关系)高并发情况下 CPU切换线程上下文损耗大
缺点的存在:JDK旧版本是没有NIO的 都是BIO通信
案例:Tomcat 7 之前都是使用BIO通信 7之后使用NIO(一个线程去维护多个操作 或者是线程池)
改进:伪NIO(也就是使用线程池去做 请求会在等待队列里)
举例1000请求,线程池数量100,等待队列500(BlockingQueue),还省400个请求怎么办?会有拒绝策列,或链接超时,比如你访问一些高峰流量网站可能会出现502提示 这就是拒绝连接