1、
Java BIO
1.1、基本介绍
Java BIO
: 同步并阻塞(传统阻塞型),服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销。
1)
Java BIO
就是传统的
java io
编程,其相关的类和接口在
java.io
;
2)
BIO
(
blocking I/O
) : 同步阻塞**,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,可以通过线程池机制改善(实现多个客户连接服务器);
3)
BIO
方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,
JDK1.4
以前的唯一选择,程序简单易理解。
1.2、
BIO
工作机制
BIO
编程简单流程
-
服务器端启动一个
ServerSocket
;
-
客户端启动
Socket
对服务器进行通信,默认情况下服务器端需要对每个客户建立一个线程与之通讯;
-
客户端发出请求后, 先咨询服务器是否有线程响应,如果没有则会等待,或者被拒绝;
-
如果有响应,客户端线程会等待请求结束后,在继续执行。
1.3、案例
实例说明:
- 使用
BIO
模型编写一个服务器端,监听
6666
端口,当有客户端连接时,就启动一个线程与之通讯。
- 要求使用线程池机制改善,可以连接多个客户端.
- 服务器端可以接收客户端发送的数据(
telnet
方式即可)。
package com.dult;import java.io.InputStream;import java.net.ServerSocket;import java.net.Socket;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;public class BIOServer {public static void main(String[] args) throws Exception {//线程池机制//思路//1. 创建一个线程池//2. 如果有客户端连接,就创建一个线程,与之通讯(单独写一个方法)ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();//创建 ServerSocketServerSocket serverSocket = new ServerSocket(6666);System.out.println(\"服务器启动了\");while (true) {System.out.println(\" 线 程 信 息 id =\" + Thread.currentThread().getId() + \" 名 字 =\" +Thread.currentThread().getName());//监听,等待客户端连接System.out.println(\"等待连接....\");final Socket socket = serverSocket.accept();System.out.println(\"连接到一个客户端\");//就创建一个线程,与之通讯(单独写一个方法)newCachedThreadPool.execute(new Runnable() {public void run() { //我们重写//可以和客户端通讯handler(socket);}});}}//编写一个 handler 方法,和客户端通讯public static void handler(Socket socket) {try {System.out.println(\" 线 程 信 息 id =\" + Thread.currentThread().getId() + \" 名 字 =\" +Thread.currentThread().getName());byte[] bytes = new byte[1024];//通过 socket 获取输入流InputStream inputStream = socket.getInputStream();//循环的读取客户端发送的数据while (true) {System.out.println(\" 线 程 信 息 id =\" + Thread.currentThread().getId() + \" 名 字 =\" +Thread.currentThread().getName());System.out.println(\"read....\");int read = inputStream.read(bytes);if(read != -1) {System.out.println(new String(bytes, 0, read)); //输出客户端发送的数据} else {break;}}}catch (Exception e) {e.printStackTrace();}finally {System.out.println(\"关闭和 client 的连接\");try {socket.close();}catch (Exception e) {e.printStackTrace();}}}}
1.4、
Java BIO
的问题分析
-
每个请求都需要创建独立的线程,与对应的客户端进行数据
Read
,业务处理,数据
Write
;
-
当并发数较大时,需要创建大量线程来处理连接,系统资源占用较大;
-
连接建立后,如果当前线程暂时没有数据可读,则线程就阻塞在
Read
操作上,造成线程资源浪费。
2、
NIO
2.1、概述
Java NIO
: 同步非阻塞,服务器实现模式为一个线程处理多个请求(连接),即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有
I/O
请求就进行处理 。
Java NIO
全称
java non-blocking IO
,是指
JDK
提供的新
API
。
NIO
相关类都被放在
java.nio
包及子包下,并且对原
java.io
包中的很多类进行改写。与传统
IO
区别:
2.2、
NIO
和
BIO
的比较
IO |
NIO |
---|---|
面向流 | 面向缓冲区(或者说面向块) |
阻塞IO | 非阻塞IO |
无 | 选择器 |
-
BIO
以流的方式处理数据,而
NIO
以块的方式处理数据,块
I/O
的效率比流
I/O
高很多
-
BIO
是阻塞的,
NIO
则是非阻塞的
-
BIO
基于字节流和字符流进行操作,而
NIO
基于
Channel
(通道)和
Buffer
(缓冲区)进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。
Selector
(选择器)用于监听多个通道的事件(比如:连接请求,数据到达等),因此使用单个线程就可以监听多个客户端通道 。
NIO
有三大核心部分:
Channel
( 通道) ,
Buffer
( 缓冲区),
Selector
( 选择器),之后会从源码级别细讲。
当线程从通道读取数据到缓冲区时,线程还是可以进行其他事情,当数据被写入缓冲区时,线程可以继续处理它,从缓冲区写入通道也类似。
通俗理解:
NIO
是可以做到用一个线程来处理多个操作的。假设有
10000
个请求过来,根据实际情况,可以分配
50
或者
100
个线程来处理。不像之前的阻塞
IO
那样,非得分配
10000
个。
而且
HTTP2.0
使用了多路复用的技术,做到同一个连接并发处理多个请求,而且并发请求的数量比
HTTP1.1
大了好几个数量级。
3、
Java AIO
Java AIO
(
NIO.2
) : 异步非阻塞,
AIO
引入异步通道的概念,采用了
Proactor
模式,简化了程序编写,有效的请求才启动线程,它的特点是先由操作系统完成后才通知服务端程序启动线程去处理,一般适用于连接数较多且连接时间较长的应用。
-
JDK 7
引入了
Asynchronous I/O
,即
AIO
。在进行
I/O
编程中,常用到两种模式:
Reactor
和
Proactor
。
Java
的
NIO
就是
Reactor
,当有事件触发时,服务器端得到通知,进行相应的处理;
-
AIO
即
NIO2.0
,叫做异步不阻塞的
IO
。
AIO
引入异步通道的概念,采用了
Proactor
模式,简化了程序编写,有效的请求才启动线程,它的特点是先由操作系统完成后才通知服务端程序启动线程去处理,一般适用于连接数较多且连接时间较长的应用。
4、
BIO
、
NIO
、
BIO
的比较
BIO | NIO | AIO | |
---|---|---|---|
IO 模型 | 同步阻塞 | 同步非阻塞(多路复用) | 异步非阻塞 |
编程难度 | 简单 | 复杂 | 复杂 |
可靠性 | 差 | 好 | 好 |
吞吐量 | 低 | 高 | 高 |
举例说明
1)同步阻塞:到理发店理发,就一直等理发师,直到轮到自己理发;
2)同步非阻塞:到理发店理发,发现前面有其它人理发,给理发师说下,先干其他事情,一会过来看是否轮到自己;
3)异步非阻塞:给理发师打电话,让理发师上门服务,自己干其它事情,理发师自己来家给你理发。