JAVA minaio模型_JAVA中IO流模型BIO,NIO,AIO

一。BIO,NIO,AIO简介

要弄懂这些io模型必须弄懂以下这些概念

同步:的是用户进程触发IO操作并等待或者轮询的去查看IO操作是否就绪 比如火车站出站口等待来深圳过年的父母 只有接到父母才能做其他事情 等待过程 不停的电话父母 火车是否到站 是否出站 直到接到父母

异步:指用户进程触发IO操作以后便开始做自己的事情,而当IO操作已经完成的时候会得到IO完成的通知(异步的特点就是通知)告诉朋友,朋友去接父母 自己忙自己的 朋友接到后 电话通知。(使用异步IO时,Java将IO读写委托给OS处理,需要将数据缓冲区地址和大小传给OS)

阻塞:所谓阻塞方式的意思是指, 当试图对该文件描述符进行读写时, 如果当时没有东西可读,或者暂时不可写, 程序就进入等待 状态,

直到有东西可读或者可写为止

非阻塞:非阻塞状态下, 如果没有东西可读, 或者不可写, 读写函数马上返回, 而不会等待,

读懂上面几个概念后就可以引入io的模型了

同步阻塞IO(JAVA BIO):

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

同步非阻塞IO(Java NIO) :同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。用户进程也需要时不时的询问IO操作是否就绪,这就要求用户进程不停的去询问。

(Java AIO(NIO.2))异步非阻塞IO:

在此种模式下,用户进程只需要发起一个IO操作然后立即返回,等IO操作真正的完成以后,应用程序会得到IO操作完成的通知,此时用户进程只需要对数据进行处理就好了,不需要进行实际的IO读写操作,因为真正的IO读取或者写入操作已经由内核完成了。

二。BIO,NIO,AIO编程实现

模拟场景  服务器接受到消息后 发回一个消息

1.BIO编程实现

BIO是同步阻塞IO流 每个获取的连接都需要创建一个新的处理线程

104e6544390c09d3fe4ef7c6a87c14cc.png

服务器端:package cn.et;

import java.io.IOException;

import java.io.InputStream;

import java.io.OutputStream;

import java.net.ServerSocket;

import java.net.Socket;

/**

* 同步阻塞模型BIO

* 使用byte[]数组缓冲数据

* @author jiaozi

*

*/

public class BIOServer {

private static ServerSocket serverSocket=null;

private static int bufferSize=4096;

private static byte[] readBuffer=new byte[bufferSize];

public static void main(String[] args) throws IOException {

serverSocket = new ServerSocket(8899);

//这里表示同步 必須等待接受到一个Socket才能执行后面的代码

while(true) {

//獲取一個客戶端連接

Socket accept = serverSocket.accept();

//必須為一個客戶端連接創建一個處理線程

DThread th=new DThread(accept);

th.start();

}

}

/**

* 處理每個連接的線程 主要讀和寫操作

* @author jiaozi

*/

static class DThread extends Thread{

Socket accept;

public DThread(Socket accept) {

this.accept = accept;

}

public void run() {

//獲取輸入流

try {

InputStream inputStream = this.accept.getInputStream();

OutputStream outputStream=this.accept.getOutputStream();

//沒有數據讀到 會阻塞直到有數據

int readCount = inputStream.read(readBuffer);

String readText=new String(readBuffer,0,readCount);

System.out.println("讀取的數據為:"+readText);

outputStream.write("hello client".getBytes());

outputStream.flush();

accept.close();

} catch (IOException e) {

e.printStackTrace();

}

}

}

}

客户端:package cn.et;

import java.io.IOException;

import java.io.InputStream;

import java.io.OutputStream;

import java.net.Socket;

import java.net.UnknownHostException;

/**

* BIO的客戶端

* @author jiaozi

*/

public class BIOClient {

private static int port=8899;

private static int bufferSize=4096;

private static byte[] readBuffer=new byte[bufferSize];

public static void main(String[] args) throws UnknownHostException, IOException {

Socket socket=new Socket("localhost",port);

OutputStream outputStream = socket.getOutputStream();

InputStream inputStream = socket.getInputStream();

outputStream.write("hello server".getBytes());

outputStream.flush();

int readCount = inputStream.read(readBuffer);

String readText=new String(readBuffer,0,readCount);

System.out.println("讀取的數據為:"+readText);

}

}演示 结果 启动BIOServer 发现BIOServer处于同步阻塞状态  启动客户端

服务端接受到消息 :讀取的數據為:hello server

客户端接受到消息 : 讀取的數據為:hello client2.NIO编程实现

Java NIO是在jdk1.4开始使用的,它既可以说成“新I/O”,也可以说成非阻塞式I/O。

下面是java NIO的工作原理:

由一个专门的线程来处理所有的 IO 事件,并负责分发。

事件驱动机制:事件到的时候触发,而不是同步的去监视事件。

线程通讯:线程之间通过 wait,notify 等方式通讯。保证每次上下文切换都是有意义的。减少无谓的线程切换。

NIO的原理

服务端和客户端各自维护一个管理通道的对象,我们称之为selector,该对象能检测一个或多个通道 (channel) 上的事件。我们以服务端为例,如果服务端的selector上注册了读事件,某时刻客户端给服务端发送了一些数据,阻塞I/O这时会调用read()方法阻塞地读取数据,而NIO的服务端会在selector中添加一个读事件。服务端的处理线程会轮询地访问selector,如果访问selector时发现有感兴趣的事件到达,则处理这些事件,如果没有感兴趣的事件到达,则处理线程会一直阻塞直到感兴趣的事件到达为止

fda67b81d94c4275430ec964c2f8a492.png

核心对象

Buffer和Channel是标准NIO中的核心对象

Buffer:是一个对象,它包含一些要写入或读出的数据。在NIO中,数据是放入buffer对象的,而在IO中,数据是直接写入或者读到Stream对象的。应用程序不能直接对 Channel 进行读写操作,而必须通过 Buffer 来进行,即 Channel 是通过 Buffer 来读写数据的。

使用 Buffer 读写数据一般遵循以下四个步骤:

写入数据到 Buffer;

调用 flip() 方法;

从 Buffer 中读取数据;

调用 clear() 方法或者 compact() 方法。

Channel:是一个对象,可以通过它读取和写入数据。可以把它看做IO中的流。但是它和流相比还有一些不同:

Channel是双向的,既可以读又可以写,而流是单向的

Channel可以进行异步的读写

对Channel的读写必须通过buffer对象

在Java NIO中Channel主要有如下几种类型:

FileChannel:从文件读取数据的

DatagramChannel:读写UDP网络协议数据

SocketChannel:读写TCP网络协议数据

ServerSocketChannel:可以监听TCP连接

服务器端:package cn.et;

import java.io.IOException;

import java.net.InetSocketAddress;

import java.nio.ByteBuffer;

import java.nio.channels.SelectionKey;

import java.nio.channels.Selector;

import java.nio.channels.ServerSocketChannel;

import java.nio.channels.SocketChannel;

import java.util.Iterator;

import java.util.Set;

/**

* NIO 同步非阻塞 該種方式 阻塞多路複用器 Selector 使用轮询方式查找是否存在io事件

* IO事件分为 OP_WRITE[缓冲区可以写入],OP_READ【缓存区有数据可读】,OP_CONNECT【客户端连接】,OP_ACCEPT[服务端接受到一个连接]

* @author jiaozi

*/

public class NIOServer {

private static ServerSocketChannel serverSocketChannel=null;

private static int bufferSize=4096;

private static ByteBuffer readBuffer=ByteBuffer.allocate(bufferSize);

private static ByteBuffer writeBuffer=ByteBuffer.allocate(bufferSize);

private static Selector selector;

public static void main(String[] args) throws IOException {

//打开通道

serverSocketChannel=ServerSocketChannel.open();

//设置为非阻塞

serverSocketChannel.configureBlocking(false);

serverSocketChannel.bind(new InetSocketAddress(8899));

selector=Selector.open();

//通道注册到多路复用器 selector中 selector监听事件 接受客户端请求事件

serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

listen();

}

public static void listen() throws IOException {

//存在注册的事件返回时 方法返回 否则一直阻塞 应该轮询

while(true) {

selector.select();

//select到了后面 说明存在事件 获取事件 并轮询

Set selectedKeys = selector.selectedKeys();

Iterator iterator = selectedKeys.iterator();

while(iterator.hasNext()) {

SelectionKey selectionKey = iterator.next();

iterator.remove();

//有客户端连接事件

if(selectionKey.isAcceptable()) {

ServerSocketChannel server = (ServerSocketChannel) selectionKey.channel();

// 获得和客户端连接的通道

SocketChannel channel = server.accept();

//这里要配置一下 否则报错

channel.configureBlocking(false);

//给接入的客户端注册读的事件

channel.register(selector, SelectionKey.OP_READ);

writeBuffer.put("hello client".getBytes());

writeBuffer.flip();

//写入测试数据

channel.write(writeBuffer);

}

//缓冲区有数据可读事件

if(selectionKey.isReadable()) {

SocketChannel channel = (SocketChannel) selectionKey.channel();

int readCount=channel.read(readBuffer);

String readText=new String(readBuffer.array(),0,readCount);

System.out.println("讀取的數據為:"+readText);

readBuffer.clear();

}

}

}

}

}

客户端:package cn.et;

import java.io.IOException;

import java.net.InetSocketAddress;

import java.nio.ByteBuffer;

import java.nio.channels.SelectionKey;

import java.nio.channels.Selector;

import java.nio.channels.SocketChannel;

import java.util.Iterator;

public class NIOClient {

private static SocketChannel socketChannel=null;

private static int bufferSize=4096;

private static ByteBuffer readBuffer=ByteBuffer.allocate(bufferSize);

private static Selector selector;

private static int port=8899;

public static void main(String[] args) throws IOException {

socketChannel=SocketChannel.open();

selector=Selector.open();

socketChannel.configureBlocking(false);

socketChannel.register(selector, SelectionKey.OP_CONNECT);

socketChannel.connect(new InetSocketAddress(port));

connect();

}

public static void connect() throws IOException {

// 轮询访问selector

while (true) {

// 选择一组可以进行I/O操作的事件,放在selector中,客户端的该方法不会阻塞,

//这里和服务端的方法不一样,查看api注释可以知道,当至少一个通道被选中时,

//selector的wakeup方法被调用,方法返回,而对于客户端来说,通道一直是被选中的

selector.select();

// 获得selector中选中的项的迭代器

Iterator ite = selector.selectedKeys().iterator();

while (ite.hasNext()) {

SelectionKey selectionKey = ite.next();

ite.remove();

//如果是连接事件

if(selectionKey.isConnectable()) {

SocketChannel channel = (SocketChannel) selectionKey.channel();

//如果正在连接 等待完成连接

if(channel.isConnectionPending()) {

channel.finishConnect();

}

channel.write(ByteBuffer.wrap(new String("hello server").getBytes()));

//在和服务端连接成功之后,为了可以接收到服务端的信息,需要给通道设置读的权限。

channel.register(selector, SelectionKey.OP_READ);

}

if(selectionKey.isReadable()) {

SocketChannel channel = (SocketChannel) selectionKey.channel();

int readCount=channel.read(readBuffer);

String readText=new String(readBuffer.array(),0,readCount);

System.out.println("讀取的數據為:"+readText);

readBuffer.clear();

}

}

}

}

}3.AIO编程实现

aio是实现了真正的异步非阻塞 将所有的io将给操作系统处理 当io事件触发时 回调用户的Handler即可 不再有nio1的轮询 linux下epol也是基于aio  aio是在jdk1.7开始实现的

服务端:package cn.et;

import java.io.IOException;

import java.net.InetSocketAddress;

import java.nio.ByteBuffer;

import java.nio.channels.AsynchronousServerSocketChannel;

import java.nio.channels.AsynchronousSocketChannel;

import java.nio.channels.CompletionHandler;

public class AIOServer {

private static AsynchronousServerSocketChannel serverSocketChannel=null;

private static int bufferSize=4096;

private static ByteBuffer readBuffer=ByteBuffer.allocate(bufferSize);

private static ByteBuffer writeBuffer=ByteBuffer.allocate(bufferSize);

public static void main(String[] args) throws IOException {

serverSocketChannel=AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(8899));

serverSocketChannel.accept(null, new CompletionHandler(){

@Override

public void completed(AsynchronousSocketChannel channel, Object attachment) {

try {

//读的异步

channel.read(readBuffer, null, new CompletionHandler() {

@Override

public void completed(Integer result, Object attachment) {

String readText=new String(readBuffer.array(),0,result);

System.out.println("讀取的數據為:"+readText);

readBuffer.clear();

}

@Override

public void failed(Throwable exc, Object attachment) {

exc.printStackTrace();

System.out.println("读取失败");

}

});

writeBuffer.put("hello client".getBytes());

writeBuffer.flip();

//写入测试数据

channel.write(writeBuffer);

serverSocketChannel.accept(null, this);// 监听新的请求,递归调用。

} catch (Exception e) {

e.printStackTrace();

}

}

@Override

public void failed(Throwable exc, Object attachment) {

System.out.println("出现异常:"+exc);

serverSocketChannel.accept(null, this);// 监听新的请求,递归调用。

}

});

System.in.read();

}

}客户端:

package cn.et;

import java.io.IOException;

import java.net.InetSocketAddress;

import java.nio.ByteBuffer;

import java.nio.channels.AsynchronousSocketChannel;

import java.nio.channels.CompletionHandler;

import java.nio.channels.Selector;

import java.util.concurrent.ExecutionException;

import java.util.concurrent.Future;

public class AIOClient {

private static AsynchronousSocketChannel socketChannel=null;

private static int bufferSize=4096;

private static ByteBuffer readBuffer=ByteBuffer.allocate(bufferSize);

private static ByteBuffer writeBuffer=ByteBuffer.allocate(bufferSize);

private static Selector selector;

private static int port=8899;

public static void main(String[] args) throws Exception {

socketChannel=AsynchronousSocketChannel.open();

//这里一定要指定ip 就算是本机 也要使用localhost

socketChannel.connect(new InetSocketAddress("localhost",port),null,new CompletionHandler() {

@Override

public void completed(Void result, Object attachment) {

try {

//读的异步

socketChannel.read(readBuffer, null, new CompletionHandler() {

@Override

public void completed(Integer result, Object attachment) {

String readText=new String(readBuffer.array(),0,result);

System.out.println("讀取的數據為:"+readText);

}

@Override

public void failed(Throwable exc, Object attachment) {

exc.printStackTrace();

System.out.println("读取失败");

}

});

//写入数据到服务端

writeBuffer.put("hello server".getBytes());

writeBuffer.flip();

//写入测试数据

socketChannel.write(writeBuffer);

} catch (Exception e) {

e.printStackTrace();

}

}

@Override

public void failed(Throwable exc, Object attachment) {

// TODO Auto-generated method stub

}

});

System.in.read();

}

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值