Java IO介绍之BIO同步阻塞模型,C10K, C10M问题
JavaIO介绍之BIO 同步阻塞模型
同步与异步调用
何为同步?
- 同步方法调用一旦开始,调用者必须等到方法调用返回后,才能继续后续的行为
何为异步?
- 异步方法调用更像一个消息传递,一旦开始,方法调用就会立即返回,调用者就可以继续后续的操作。而,异步方法通常会在另外一个线程中,“真实”地执行着。整个过程,不会阻碍调用者的工作
- 图解
同步/异步不能与阻塞/非阻塞混淆
-
阻塞和非阻塞 强调的是程序在等待调用结果(消息,返回值)时的状态. 阻塞调用是指调用结果返回之前,当前线程会被挂起。调用线程只有在得到结果之后才会返回。非阻塞调用指在不能立刻得到结果之前,该调用不会阻塞当前线程。 对于同步调用来说,很多时候当前线程还是激活的状态,只是从逻辑上当前函数没有返回而已,即同步等待时什么都不干,白白占用着资源。
-
同步和异步强调的是消息通信机制 (synchronous communication/ asynchronous communication)。所谓同步,就是在发出一个"调用"时,在没有得到结果之前,该“调用”就不返回。但是一旦调用返回,就得到返回值了。换句话说,就是由“调用者”主动等待这个“调用”的结果。而异步则是相反,"调用"在发出之后,这个调用就直接返回了,所以没有返回结果。换句话说,当一个异步过程调用发出后,调用者不会立刻得到结果。而是在"调用"发出后,"被调用者"通过状态、通知来通知调用者,或通过回调函数处理这个调用
了解完概念后继续看BIO对应的Java BIO编程模型
- 在JDK1.4之前,建立网络通信采用的BIO模型,为啥称之为同步阻塞模型, accept,read/write,connect,客户端发起请求后,必须有个对应服务端线程来处理客户端请求, 而线程资源是有限的,一旦客户端并发请求过大,经典面试题会到C10k,C10M问题
- Socket Client
package com.cnblogs.cx.net;
import java.io.*;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;
public class TCPClient {
public static void main(String[] args) throws IOException {
String host = "127.0.0.1";
Socket socket = new Socket(InetAddress.getByName(host),9999);
OutputStream outputStream = socket.getOutputStream();
/*FileInputStream fis = new FileInputStream(new File("test.png"));
byte[] buffer = new byte[1024];
int len ;
while(( len = fis.read(buffer))!=-1){
outputStream.write(buffer,0,len);
}*/
//while(true) {
Scanner scanner = new Scanner(System.in);
String inStr = scanner.nextLine();
outputStream.write(inStr.getBytes());
// socket.shutdownOutput();
InputStream inputStream = socket.getInputStream();
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] bs = new byte[1024];
int len2;
while ((len2 = inputStream.read(bs)) != -1) {
bos.write(bs, 0, len2);
}
System.out.println(bos.toString());
//}
outputStream.close();
inputStream.close();
bos.close();
socket.close();
}
}
- Socket Server
package com.cnblogs.cx.net;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
public class TCPServer {
public static void main(String[] args) throws Exception {
ServerSocket serverSocket = new ServerSocket(9999);
Socket socket = null;
InputStream is = null;
ByteArrayOutputStream bos = null;
OutputStream os = null;
while(true) {
socket = serverSocket.accept();
is = socket.getInputStream();
bos = new ByteArrayOutputStream();
byte[] by = new byte[1024];
int len;
while ((len = is.read(by)) != -1) {
bos.write(by, 0, len);
}
System.out.println(bos.toString());
/*FileOutputStream fos = new FileOutputStream(new File("recive.jpg"));
byte[] by = new byte[1024];
int len ;
while ((len = is.read(by))!=-1){
fos.write(by,0,len);
}*/
Scanner scanner = new Scanner(System.in);
String str = scanner.nextLine();
os = socket.getOutputStream();
os.write("str".getBytes());
}
/* is.close();
bos.close();
os.close();
socket.close();
serverSocket.close();*/
}
}
- 为了提高客户端的并发请求,优化方式可以将阻塞的地方拆分,引入多线程; Java编程模型解决方案如下:
- 服务端编程:
public class MutilThreadBIOServer {
public static void main(String[] args) {
try {
//创建serverSocket实例
ServerSocket serverSocket = new ServerSocket();
//绑定端口
serverSocket.bind(new InetSocketAddress(9999));
System.out.println("服务端启动了");
while (true) {
//等待多个客户端的连接
Socket socket = serverSocket.accept();
System.out.println("客户端:"+socket.getRemoteSocketAddress()+" 上线了");
//将Socket实例交给子线程处理
new ServerHandler(socket).start();
}
//关闭资源
// serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public class ServerHandler extends Thread{
private Socket socket;
/**
* 构造函数,将socket实例传递给子线程
* @param socket
*/
public ServerHandler(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
//读取客户端的消息
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//回复消息
OutputStream outputStream = socket.getOutputStream();
String msg = null;
while ((msg = reader.readLine())!= null) {
System.out.println("客户端:"+socket.getRemoteSocketAddress()+" 消息:"+msg);
//给客户端回复消息
outputStream.write((msg+"\n").getBytes());
//循环结束条件
if ("".equals(msg) || "exit".equals(msg)) break;
}
//关闭资源
reader.close();
outputStream.close();
System.out.println("客户端:"+socket.getRemoteSocketAddress()+" 关闭");
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
- 客户端编程
//启动socket实例
Socket socket = new Socket();
try {
//连接服务端
socket.connect(new InetSocketAddress("127.0.0.1",9999));
System.out.println("客户端连接服务端成功");
//进行读写操作
OutputStream outputStream = socket.getOutputStream();
outputStream.write("hello\n".getBytes());
outputStream.flush();
//读数据
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String msg = reader.readLine();
System.out.println(msg);
//关闭资源
reader.close();
outputStream.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
总结: 了解Java BIO同步阻塞模型, 熟悉Socket同步阻塞通讯方式,C10K,C10M问题。