Ⅰ java中BIO,NIO和AIO的区别和应用场景
Reactor and Proactor
IO读写时,多路复用机制都会依赖对一个事件多路分离器,负责把源事件的IO 事件分离出来,分别到相应的read/write事件分离器。涉及到事件分离器的两种模式分别就是 Reactor和Proactor,Reactor是基于同步IO的,Proactor是基于异步IO的。
在Reactor模式中,事件分离者等待某个事件或者可应用或个操作的状态发生(比如文件描述符可读写,或者是socket可读写),事件分离者就把这个事件传给事先注册的事件处理函数或者回调函数,由后者来做实际的读写操作。
Ⅱ java的API中有哪些常用的包
Application Programming Interface 应用程序编程接口,Java的api就多的数不清了,平时编程用的都是API。
Ⅲ java中bio nio aio的区别和联系
BIO是一个连接一个线程。
NIO是一个请求一个线程。
AIO是一个有效请求一个线程。
先来个例子理解一下概念,以银行取款为例:
同步 : 自己亲自出马持银行卡到银行取钱(使用同步IO时,Java自己处理IO读写);
异步 : 委托一小弟拿银行卡到银行取钱,然后给你(使用异步IO时,Java将IO读写委托给OS处理,需要将数据缓冲区地址和大小传给OS(银行卡和密码),OS需要支持异步IO操作API);
阻塞 : ATM排队取款,你只能等待(使用阻塞IO时,Java调用会一直阻塞到读写完成才返回);
非阻塞 : 柜台取款,取个号,然后坐在椅子上做其它事,等号广播会通知你办理,没到号你就不能去,你可以不断问大堂经理排到了没有,大堂经理如果说还没到你就不能去(使用非阻塞IO时,如果不能读写Java调用会马上返回,当IO事件分发器会通知可读写时再继续进行读写,不断循环直到读写完成)
Java对BIO、NIO、AIO的支持:
Java BIO : 同步并阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。
Java NIO : 同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。
Java AIO(NIO.2) : 异步非阻塞,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理,
Ⅳ java Nio读写为什么是双向
作者:美团技术团队
链接:https://zhuanlan.hu.com/p/23488863
来源:知乎
着作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
NIO(Non-blocking I/O,在Java领域,也称为New I/O),是一种同步非阻塞的I/O模型,也是I/O多路复用的基础,已经被越来越多地应用到大型应用服务器,成为解决高并发与大量连接、I/O处理问题的有效方式。
那么NIO的本质是什么样的呢?它是怎样与事件模型结合来解放线程、提高系统吞吐的呢?
本文会从传统的阻塞I/O和线程池模型面临的问题讲起,然后对比几种常见I/O模型,一步步分析NIO怎么利用事件模型处理I/O,解决线程池瓶颈处理海量连接,包括利用面向事件的方式编写服务端/客户端程序。最后延展到一些高级主题,如Reactor与Proactor模型的对比、Selector的唤醒、Buffer的选择等。
注:本文的代码都是伪代码,主要是为了示意,不可用于生产环境。
传统BIO模型分析
让我们先回忆一下传统的服务器端同步阻塞I/O处理(也就是BIO,Blocking I/O)的经典编程模型:
{
ExecutorService executor = Excutors.newFixedThreadPollExecutor(100);//线程池
ServerSocket serverSocket = new ServerSocket();
serverSocket.bind(8088);
while(!Thread.currentThread.isInturrupted()){//主线程死循环等待新连接到来
Socket socket = serverSocket.accept();
executor.submit(new ConnectIOnHandler(socket));//为新的连接创建新的线程
}
class ConnectIOnHandler extends Thread{
private Socket socket;
public ConnectIOnHandler(Socket socket){
this.socket = socket;
}
public void run(){
while(!Thread.currentThread.isInturrupted()&&!socket.isClosed()){死循环处理读写事件
String someThing = socket.read()....//读取数据
if(someThing!=null){
......//处理数据
socket.write()....//写数据
}
}
}
}
这是一个经典的每连接每线程的模型,之所以使用多线程,主要原因在于socket.accept()、socket.read()、socket.write()三个主要函数都是同步阻塞的,当一个连接在处理I/O的时候,系统是阻塞的,如果是单线程的话必然就挂死在那里;但CPU是被释放出来的,开启多线程,就可以让CPU去处理更多的事情。其实这也是所有使用多线程的本质:
利用多核。
当I/O阻塞系统,但CPU空闲的时候,可以利用多线程使用CPU资源。
现在的多线程一般都使用线程池,可以让线程的创建和回收成本相对较低。在活动连接数不是特别高(小于单机1000)的情况下,这种模型是比较不错的,可以让每一个连接专注于自己的I/O并且编程模型简单,也不用过多考虑系统的过载、限流等问题。线程池本身就是一个天然的漏斗,可以缓冲一些系统处理不了的连接或请求。
不过,这个模型最本质的问题在于,严重依赖于线程。但线程是很"贵"的资源,主要表现在:
线程的创建和销毁成本很高,在Linux这样的操作系统中,线程本质上就是一个进程。创建和销毁都是重量级的系统函数。
线程本身占用较大内存,像Java的线程栈,一般至少分配512K~1M的空间,如果系统中的线程数过千,恐怕整个JVM的内存都会被吃掉一半。
线程的切换成本是很高的。操作系统发生线程切换的时候,需要保留线程的上下文,然后执行系统调用。如果线程数过高,可能执行线程切换的时间甚至会大于线程执行的时间,这时候带来的表现往往是系统load偏高、CPU sy使用率特别高(超过20%以上),导致系统几乎陷入不可用的状态。
容易造成锯齿状的系统负载。因为系统负载是用活动线程数或CPU核心数,一旦线程数量高但外部网络环境不是很稳定,就很容易造成大量请求的结果同时返回,激活大量阻塞线程从而使系统负载压力过大。
所以,当面对十万甚至百万级连接的时候,传统的BIO模型是无能为力的。随着移动端应用的兴起和各种网络游戏的盛行,百万级长连接日趋普遍,此时,必然需要一种更高效的I/O处理模型。
NIO是怎么工作的
很多刚接触NIO的人,第一眼看到的就是Java相对晦涩的API,比如:Channel,Selector,Socket什么的;然后就是一坨上百行的代码来演示NIO的服务端Demo……瞬间头大有没有?
我们不管这些,抛开现象看本质,先分析下NIO是怎么工作的。
常见I/O模型对比
所有的系统I/O都分为两个阶段:等待就绪和操作。举例来说,读函数,分为等待系统可读和真正的读;同理,写函数分为等待网卡可以写和真正的写。
需要说明的是等待就绪的阻塞是不使用CPU的,是在“空等”;而真正的读写操作的阻塞是使用CPU的,真正在"干活",而且这个过程非常快,属于memory ,带宽通常在1GB/s级别以上,可以理解为基本不耗时。
下图是几种常见I/O模型的对比:
密码:380p以上都是小编收集了大神的灵药,喜欢的拿走吧!喜欢小编就轻轻关注一下吧!
Ⅳ JAVA NIO 和 AIO 的区别
Java NIO : 同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。
Java AIO(NIO.2) : 异步非阻塞,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理,
NIO方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4开始支持。
AIO方式使用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂,JDK7开始支持
I/O属于底层操作,需要操作系统支持,并发也需要操作系统的支持,所以性能方面不同操作系统差异会比较明显。另外NIO的非阻塞,需要一直轮询,也是一个比较耗资源的。所以出现AIO
Ⅵ 如果你是一个 Java 面试官,你会问哪些问题
1、谈谈你对 Java 平台的理解?“Java 是解释执行”,这句话正确吗?考点分析:对于这类笼统的问题,你需要尽量表现出自己的思维深入并系统化,Java 知识理解得也比较全面,一定要避免让面试官觉得你是个“知其然不知其所以然”的人。毕竟明白基本组成和机制,是日常工作中进行问题诊断或者性能调优等很多事情的基础,相信没有招聘方会不喜欢“热爱学习和思考”的面试者。回归正题,对于 Java 平台的理解,可以从很多方面简明扼要地谈一下,例如:Java 语言特性,包括泛型、Lambda 等语言特性;基础类库,包括集合、IO/NIO、网络、并发、安全等基础类库。对于我们日常工作应用较多的类库,面试前可以系统化总结一下,有助于临场发挥。2、对比Hashtable、HashMap、TreeMap有什么不同?考点分析:上面的回答,只是对一些基本特征的简单总结,针对Map相关可以扩展的问题很多,从各种数据结构、典型应用场景,到程序设计实现的技术考量,尤其是在Java 8里,HashMap本身发生了非常大的变化,这些都是经常考察的方面。很多朋友向我反馈,面试官似乎钟爱考察HashMap的设计和实现细节,所以今天我会增加相应的源码解读,主要专注于下面几个方面:理解Map相关类似整体结构,尤其是有序数据结构的一些要点。从源码去分析HashMap的设计和实现要点,理解容量、负载因子等,为什么需要这些参数,如何影响Map的性能,实践中如何取舍等。理解树化改造的相关原理和改进原因。除了典型的代码分析,还有一些有意思的并发相关问题也经常会被提到,如HashMap在并发环境可能出现无限循环占用CPU、size不准确等诡异的问题。我认为这是一种典型的使用错误,因为HashMap明确声明不是线程安全的数据结构,如果忽略这一点,简单用在多线程场景里,难免会出现问题。理解导致这种错误的原因,也是深入理解并发程序运行的好办法。对于具体发生了什么,你可以参考这篇很久以前的分析,里面甚至提供了示意图,我就不再重复别人写好的内容了。3、Java 提供了哪些 IO 方式? NIO 如何实现多路复用?考点分析:在实际面试中,从传统 IO 到 NIO、NIO 2,其中有很多地方可以扩展开来,考察点涉及方方面面,比如:基础 API 功能与设计, InputStream/
Ⅶ java nio多路复用是什么意思
就是NIO库可以利用Selector多路复用各个Socket连接。
提高连接效率,降低连接的阻塞。
Ⅷ java网络io模型有几种
#BIO---Blocking IO
- 每个socket一个线程,读写时线程处于阻塞状态。
优点:实现简单
缺点:无法满足高并发,高接入的需求
- 不使用线程池的BIO模型,除了无法满足高并发需求外,由于需要为每个请求创建一个线程,还可能因为接入大量不活跃连接而耗尽服务器资源。
- 使用线程池的BIO模型,虽然控制了线程数量,但由于其本质上读写仍是阻塞的,仍无法满足高并发需求。
#NIO---Non-Blocking IO(非阻塞IO)
##非阻塞IO和多路复用
非阻塞IO和多路复用实际上是两个不用的概念,由于两者通常结合在一起使用,因此两者往往被混为一谈。下面我将试着分清这两个概念:
###非阻塞IO
与BIO相对应,非阻塞IO的读写方法无论是否有数据都立即返回,因此可以通过轮询方式来实现,但轮询方式的效率并不比BIO有显着提高,因为每个连接仍然需要占用一个线程。下面是轮询方式实现的IO模式图:
###多路复用
- 多路复用结合非阻塞IO能够明显提高IO的效率,这也是Java1.4把非阻塞IO和多路复用同时发布的原因。
- 多路复用的核心是多路复用器(Selector),它是需要操作系统底层支持的,简单的说,就是进程把多个socket和它们关心的事件(比如连接请求或数据已准备好)都注册在多路复用器上,操作系统会在事件发生时通知多路复用器,这样进程就可以通过多路复用器知道在那个socket上发生了什么时间,从而进行对应的处理。
- 多路复用的优点在于只需要一个线程监测(阻塞或轮询方式均可)多路选择器的状态,只有在有事件需要发生时才会真正的创建线程进行处理,因此更适合高并发多接入的应用环境。
- 在Linux系统下,多路复用的底层实现是epoll方法,与select/poll的顺序扫描不同,epoll采用效率更高的事件驱动方式,而且epoll方式并没有socket个数限制。
##BIO和NIO的比较
- BIO适用于连接长期保持的应用,比如一个复杂系统中模块之间通过长连接来进行通信。
- NIO加多路复用的模式更适合短连接、高并发、多接入的情形,比如网络服务器。
##NIO网络编程的常用接口
##Reactor模式
Reactor模式用于解决事件分发处理的问题,Handler把自己的channel和关注的事件注册到Selector中,当对应的事件发生在自己的channel上时,对应的handler就会得到通知并进行处理。
- 单线程的Reactor
消息的分发、读写、处理都在一个线程中处理,是Reactor最简单的实现方式,如果消息的处理需要较长时间,会影响效率。
```java
//Reactor类,负责分发事件并调用对应的handler
class Reactor implements Runnable {
final Selector selector;
final ServerSocketChannel serverSocket;
//Reactor初始化
Reactor(int port) throws IOException {
selector = Selector.open();
serverSocket = ServerSocketChannel.open();
serverSocket.socket().bind(new InetSocketAddress(port));
serverSocket.configureBlocking(false); //必须配置为非阻塞
//Acceptor会在Reactor初始化时就注册到Selector中,用于接受connect请求
SelectionKey sk = serverSocket.register(selector, SelectionKey.OP_ACCEPT);
sk.attach(new Acceptor()); //attach callback object, Acceptor
}
//分发消息并调用对应的handler
public void run() {
try {
while (!Thread.interrupted()) {
selector.select();
Set selected = selector.selectedKeys();
Iterator it = selected.iterator();
while (it.hasNext())
dispatch((SelectionKey)(it.next()); //Reactor负责dispatch收到的事件
selected.clear();
}
} catch (IOException ex) { /* ... */ }
}
void dispatch(SelectionKey k) {
Runnable r = (Runnable)(k.attachment()); //调用之前注册的callback对象
if (r != null)
r.run();
}
//Acceptor也是一个handler,负责创建socket并把新建的socket也注册到selector中
class Acceptor implements Runnable { // inner
public void run() {
try {
SocketChannel c = serverSocket.accept();
if (c != null)
new Handler(selector, c);
}
catch(IOException ex) { /* ... */ }
}
}
}
//Concrete Handler:用于收发和处理消息。
//在当前的实现中,使用Runnable接口作为每个具体Handler的统一接口
//如果在处理时需要参数和返回值,也可以为Handler另外声明一个统一接口来代替Runnable接口
final class Handler implements Runnable {
final SocketChannel socket;
final SelectionKey sk;
ByteBuffer input = ByteBuffer.allocate(MAXIN);
ByteBuffer output = ByteBuffer.allocate(MAXOUT);
static final int READING = 0, SENDING = 1;
int state = READING;
Handler(Selector sel, SocketChannel c) throws IOException {
socket = c; c.configureBlocking(false);
// Optionally try first read now
sk = socket.register(sel, 0);
sk.attach(this); //将Handler作为callback对象
sk.interestOps(SelectionKey.OP_READ); //第二步,接收Read事件
sel.wakeup();
}
boolean inputIsComplete() { /* ... */ }
boolean outputIsComplete() { /* ... */ }
void process() { /* ... */ }
public void run() {
try {
if (state == READING) read();
else if (state == SENDING) send();
} catch (IOException ex) { /* ... */ }
}
void read() throws IOException {
socket.read(input);
if (inputIsComplete()) {
process();
state = SENDING;
// Normally also do first write now
sk.interestOps(SelectionKey.OP_WRITE); //第三步,接收write事件
}
}
void send() throws IOException {
socket.write(output);
if (outputIsComplete()) sk.cancel(); //write完就结束了, 关闭select key
}
}
//上面 的实现用Handler来同时处理Read和Write事件, 所以里面出现状态判断
//我们可以用State-Object pattern来更优雅的实现
class Handler { // ...
public void run() { // initial state is reader
socket.read(input);
if (inputIsComplete()) {
process();
sk.attach(new Sender()); //状态迁移, Read后变成write, 用Sender作为新的callback对象
sk.interest(SelectionKey.OP_WRITE);
sk.selector().wakeup();
}
}
class Sender implements Runnable {
public void run(){ // ...
socket.write(output);
if (outputIsComplete()) sk.cancel();
}
}
}
```
- 多线程Reacotr
处理消息过程放在其他线程中执行
```java
class Handler implements Runnable {
// uses util.concurrent thread pool
static PooledExecutor pool = new PooledExecutor(...);
static final int PROCESSING = 3;
// ...
synchronized void read() { // ...
socket.read(input);
if (inputIsComplete()) {
state = PROCESSING;
pool.execute(new Processer()); //使用线程pool异步执行
}
}
synchronized void processAndHandOff() {
process();
state = SENDING; // or rebind attachment
sk.interest(SelectionKey.OP_WRITE); //process完,开始等待write事件
}
class Processer implements Runnable {
public void run() { processAndHandOff(); }
}
}
```
- 使用多个selector
mainReactor只负责处理accept并创建socket,多个subReactor负责处理读写请求
```java
Selector[] selectors; //subReactors集合, 一个selector代表一个subReactor
int next = 0;
class Acceptor { // ...
public synchronized void run() { ...
Socket connection = serverSocket.accept(); //主selector负责accept
if (connection != null)
new Handler(selectors[next], connection); //选个subReactor去负责接收到的connection
if (++next == selectors.length) next = 0;
}
}
```
#AIO
AIO是真正的异步IO,它于JDK1.7时引入,它和NIO的区别在于:
- NIO仍然需要一个线程阻塞在select方法上,AIO则不需要
- NIO得到数据准备好的消息以后,仍然需要自己把消息复制到用户空间,AIO则是通过操作系统的支持把数据异步复制到用户空间以后再给应用进程发出信号。
Ⅸ java.nio.channels的多路复用
非阻塞 I/O
描述
SelectableChannel 可实现多路复用的通道
DatagramChannel java .net.DatagramSocket 通道
Pipe.SinkChannel 对管道的写入结束
Pipe.SourceChannel 对管道的读取结束
ServerSocketChannel java .net.ServerSocket 通道
SocketChannel java .net.Socket 通道
Selector 可选择通道的多路复用器
SelectionKey 表示通道注册到选择器的标记
Pipe 形成单向管道的两个通道
多路复用的非阻塞 I/O 比面向线程的阻塞 I/O 的可伸缩性更好,由选择器、可选择通道 和选择键 提供。
选择器 是可选择通道 的多路复用器,它是可被置于非阻塞模式 的特殊类型的通道。要执行多路复用的 I/O 操作,首先要创建一个或多个可选择通道、将其置于非阻塞模式,并将注册 到选择器注册一个通道会指定一组由选择器测试其是否准备就绪的 I/O 操作,并返回一个表示该注册的选择键。
一旦已经向选择器注册了通道,就可执行选择操作 以发现哪些通道(如果有)已经准备好执行先前已声明感兴趣的一个或多个操作。如果某个通道已准备就绪,则将注册时所返回的键添加到该选择器的已选择键集中。为了确定每个通道已准备好执行哪些操作,可以检查该键集和其中的键。为了执行所需的任何 I/O 操作,可根据每个键检索相应的通道。
指示其通道对某个操作已准备就绪的选择键只是一个提示,并不保证线程执行此种操作而不导致被阻塞。为了在这些提示证明不正确时忽略这些提示,则要强制写入负责执行多路复用 I/O 操作的代码。
此包定义了与 java .net 包中定义的 DatagramSocket、ServerSocket 和 Socket 类相对应的可选择通道类。为了支持与通道相关的套接字,已经对这些类进行了较小的更改。此包还定义了实现单向管道的简单类。在所有情况下,都是通过调用相应类的静态 open 方法来创建新的可选择通道。如果某个通道需要一个关联的套接字,则此操作同时也创建一个套接字。
可通过“插入”一个在 java.nio.channels.spi 包中定义的 SelectorProvider 类的替代定义或实例来替换选择器、可选通道和选择键的实现。并不期望很多开发人员都会实际使用这些设施;提供它的主要目的是为了在需要极高的性能时,能够让经验丰富的用户能充分利用特定于操作系统的 I/O 多路复用机制。
实现多路复用 I/O 抽象所要求的很多簿记和同步任务由 java.nio.channels.spi 包中的 AbstractInterruptibleChannel、AbstractSelectableChannel、AbstractSelectionKey 和 AbstractSelector 类来执行。在定义一个自定义的选择器提供程序时,应该只直接扩展 AbstractSelector 和 AbstractSelectionKey 类;自定义通道类应该扩展此包中定义的适当 SelectableChannel 子类。
从以下版本开始:
1.4