1 BIO同步阻塞
1.1 介绍
-
Java BIO就是传统的java io编程,其相关的类和接口在java.io
-
BIO(blocking l/0):同步阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,可以通过线程池机制改善(实现多个客户连接服务器。)
-
BIO方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4以前的唯一选择,程序简单易理解
简单流程:
【1】 服务端启动一个ServerSocket
【2】客户端启动Socket对服务端进行通讯,默认情况下服务端需要对每个客户建立一个线程与之通讯
【3】客户端发出请求后,先咨询服务器是否有线程响应,如果没有则会等待或者被拒绝
【4】如果有响应,客户端线程会等待请求结束后,再继续执行
每个客户socket是一个线程,read之前这个线程也是阻塞的
Java BIO问题分析
-
每个请求都需要创建独立的线程,与对应的客户端进行数据Read,业务处理,数据Write 。
-
当并发数较大时,需要创建大量线程来处理连接,系统资源占用较天。
-
连接建立后,如果当前线程暂时没有数据可读,则线程就阻塞在 Read操作上,造成线程资源浪费
1.2 代码
package personal.netty.bio;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @author LanceQ
* @date 2022年03月06日 17:09
*/
public class BioServer {
public static void main(String[] args) throws IOException {
//1.创建一个线程池
//2.如果有客户端连接,就创建一个线程,与之通讯
ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
ServerSocket serverSocket = new ServerSocket(6666);
System.out.println("服务器启动了");
while (true){
//主线程一直处于监听的状态
printThread();
//监听,等待客户端连接(每创建完一个线程,会回到这里,启动的时候会在这里)
System.out.println("等待连接·············");
final Socket accept = serverSocket.accept();
System.out.println("连接到客户端");
//创建一个线程池,与之通讯
newCachedThreadPool.execute(()->{
handler(accept);
//终端窗口
//cmd (telnet 127.0.0.1 6666)
//ctrl+] :ctrl加一个中括号
//send hello 100
});
}
}
/**
* 编写一个handler方法和客户端进行通讯
*/
public static void handler(Socket socket){
byte[] bytes = new byte[1024];
try {
printThread();
//通过socket获取输入流
InputStream inputStream = socket.getInputStream();
//循环读取客户端发送的数据(
while (true){
printThread();
//写进bytes=====================>(没有数据会阻塞在这里)
// (有请求进行先执行inputStream.read(bytes),执行完再执行printThread打印线程信息,
// 然后回到inputStream.read(bytes)等待下一次的请求)
//有连接的话,会阻塞在这里(每接收完一次数据会停留在这里)
System.out.println("read·············");
int read = inputStream.read(bytes);
//如果read不等于-1表示可以继续读
if(read!=-1){
//输出客户端发送的数据
System.out.println(new String(bytes,0,read));
}else {
break;
}
}
} catch (IOException e) {
e.printStackTrace();
}finally {
System.out.println("关闭和client连接");
closeSocket(socket);
}
}
/**
* 输出线程信息
*/
private static void printThread() {
System.out.println("线程信息 id = "+
Thread.currentThread().getId()+" 名字="+
Thread.currentThread().getName());
}
/**
* 关闭连接
*/
private static void closeSocket(Socket socket) {
if(socket!=null){
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
window系统
//终端窗口
//cmd (telnet 127.0.0.1 6666)
//ctrl+] :ctrl加一个中括号
//send hello 100
运行结果:
服务器启动了
线程信息 id = 1 名字=main
等待连接·············
连接到客户端
线程信息 id = 1 名字=main
等待连接·············
线程信息 id = 13 名字=pool-1-thread-1
线程信息 id = 13 名字=pool-1-thread-1
read·············
������
线程信息 id = 13 名字=pool-1-thread-1
read·············
hello world
线程信息 id = 13 名字=pool-1-thread-1
read·············
连接到客户端
线程信息 id = 1 名字=main
等待连接·············
线程信息 id = 14 名字=pool-1-thread-2
线程信息 id = 14 名字=pool-1-thread-2
read·············
second 100
线程信息 id = 14 名字=pool-1-thread-2
read·············
参考:视频