java bio例子_Java BIO 编程

Java BIO 基本介绍

Java BIO 就是传统的java io 编程,其相关的类和接口在 java.io

BIO(blocking I/O) : 同步阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,可以通过线程池机制改善(实现多个客户连接服务器)。

BIO方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4以前的唯一选择,程序简单易理解

Java BIO 工作机制

​采用 BIO 通信模型 的服务端,通常由一个独立的 Acceptor 线程负责监听客户端的连接。我们一般通过在while(true) 循环中服务端会调用 ServerSocket的accept() 方法等待接收客户端的连接的方式监听请求,一旦接收到一个连接请求,就可以建立通信套接字Socket,并为这个通信套接字创建一个新的客户端线程处理这条 Socket 链路。如下图所示。

230620.png

​但是在池化技术大行其道的今天,线程的创建和销毁时非常浪费资源的,使用线程池将线程利用起来重复使用,线程池可以设置等待队列的大小和最大线程数,因此,它的资源占用是可控的,无论多少个客户端并发访问,都不会导致资源的耗尽和宕机。那么上面的传统 BIO 工作机制图就变成了如下图所示的演进。

230619.png

说了这么多很多刚入门的小伙伴依旧很懵逼,看不懂。下面我举一个生活中的例子:

传统的 BIO 模型工作机制 -:在生活中饭店一般都有大门口接待服务员(类似于 Acceptor 线程 )接待所有进入饭店的客人,当有一个客人进来吃饭后,饭店就招聘一个服务员(类似于创建线程)专门接待这桌客人,这个服务员在此期间不能做其他事情,当客人走后饭店就把这个服务员辞退(类似于销毁线程),有点类似于同生共死,当然世界上没有那家饭店愿意做这种事情,太不现实;

线程池技术优化后的 BIO 模型工作机制:饭店都会有固定的多个服务员,当有一个客人进来吃饭后,就指定某个服务员专门接待这桌客人(类似于从线程池中取出一个线程),服务员在此期间也不能做其他时间,但是不同的是,当客人走后就可以让这个服务员休息一会(类似于将线程放回线程池等待下次使用),等待下一桌客人的到来。

Java BIO 应用实例

1、代码实现思路

使用线程池替代单个线程,每建立一个客户端连接就从线程池中取出一个线程

serverSocket的accept函数监听客户端连接,如果没有客户端连接,程序将阻塞在accept函数上

创建handler函数,处理Socket链路数据,接收来自客户端发送的请求,并打印在控制台

2、服务端代码实现

import java.io.BufferedReader;

import java.io.IOException;

import java.io.InputStreamReader;

import java.net.ServerSocket;

import java.net.Socket;

import java.util.concurrent.ArrayBlockingQueue;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.ThreadPoolExecutor;

import java.util.concurrent.TimeUnit;

/**

* @Description: 基于BIO通信模型的TimeServer

* @Author: hh

* @date 2020/10/14

*/

public class TimeServer {

//1、创建一个线程池,如果有客户端连接就创建一个线程

private static final ExecutorService threadPool = new ThreadPoolExecutor(

10,

10,

120L,

TimeUnit.SECONDS,

new ArrayBlockingQueue<>(100));

public static void main(String[] args) {

//1、指定监听端口

int port = 9999;

try (ServerSocket serverSocket = new ServerSocket(port);) {

System.out.println("服务端启动,监听端口: " + port);

while (true) {

//2、创建一个无限循环监听客户端连接,如果没有客户端接入,则主线程将阻塞在 accept() 函数上

System.out.println("等待连接。。。");

Socket socket = serverSocket.accept();

System.out.println("连接到一个客户端,主机地址:" + socket.getInetAddress().getHostAddress());

threadPool.execute(() -> {

handler(socket);

});

}

} catch (IOException e) {

e.printStackTrace();

}

}

/**

* Socket链路处理器

* @param socket

*/

private static void handler(Socket socket) {

try (BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()))) {

System.out.println(Thread.currentThread().getName() + " - 接收来自客户端信息");

String body = null;

//3、循环读取客户端发送信息

while (true) {

if ((body = reader.readLine()) == null) {

break;

}

//4、输出客户端发送信息

System.out.println("服务端接收来自客户端信息:" + body);

}

} catch (IOException e) {

e.printStackTrace();

}

}

}

3、客户端发起请求

1、启动 TimeServer,控制台输出如下:

230626.png

2、如果是win10系统电脑,使用 win+R命令打开运行窗口,输入cmd命令

230631.png

3、点击"确定",进入命令行窗口,输入"telnet 127.0.0.1 9999",TimeServer控制台输出如下

230634.png

4、ctrl + ] 命令回显内容,回车,进入编辑状态,输入任何内容,TimeServer控制台将会输出相应内容

230637.png

BIO 模型问题分析

每个请求都需要创建独立的线程,与对应的客户端进行数据 Read,业务处理,数据 Write 。

当并发数较大时,需要创建大量线程来处理连接,系统资源占用较大。

连接建立后,如果当前线程暂时没有数据可读,则线程就阻塞在 Read 操作上,造成线程资源浪费

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值