从同步与异步&阻塞与非阻塞的概念,到具体的I/O模型,再到具体的Java语言实现,都是层层递进,本篇就从Java语言来看I/O模型的大概情况。
整个Java I/O模型,大致可以分为三类
BIO,即为Blocking I/O,阻塞IO,大致流程为:
public class BlockingIOServer {
public static void main(String[] args) throws IOException {
int port = 10000;
ExecutorService threadPool = Executors.newFixedThreadPool(10);
ServerSocket server = new ServerSocket(port);
while(true){
Socket client = server.accept();
//从线程池取线程处理client
threadPool.execute(()->{
try{
InputStream input = client.getInputStream();
//TODO read input
String req = null;
String res = "response:"+req;
//TODO response
client.getOutputStream().write(res.getBytes());
}catch(IOException e){
e.printStackTrace();
}finally {
try {
client.close();
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
}
}
现在tomcat I/O模型默认还是BIO。
但是连接不大,该模型还是非常具有优越性,代码编写简单,只需要关注该线程内的连接即可。
BIO模型,也就是同步非阻塞模型。
NIO,即是Non Blocking I/O,非阻塞IO。
在JDK1.4及以后版本中提供了一套API来专门操作非阻塞I/O,接口以及类定义在java.nio包。由于这套API是JDK新提供的I/O API,因此,也叫New I/O。
NIO 的工作大致流程为:
public class NonBlockingIOServer {
private int BLOCK = 4096;
private ByteBuffer sendBuffer = ByteBuffer.allocate(BLOCK);
private ByteBuffer receiveBuffer = ByteBuffer.allocate(BLOCK);
private Selector selector;
public NonBlockingIOServer(int port) throws IOException {
//1.open ServerSocketChannel
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
//2.configureBlocking false
serverSocketChannel.configureBlocking(false);
//3.bind port
serverSocketChannel.socket().bind(new InetSocketAddress(port));
//4.open Selector
selector = Selector.open();
//5.serverSocketChannel register select
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("Server Start,port:"+port);
}
private void accept() throws IOException {
while (true) {
// 1.select,block
selector.select();
// 2.SelectionKey iterator
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()) {
SelectionKey selectionKey = iterator.next();
iterator.remove();
try {
doAccept(selectionKey);
} catch (IOException e) {
selectionKey.cancel();
e.printStackTrace();
}
}
}
}
private void doAccept(SelectionKey selectionKey)throws IOException{
if (selectionKey.isAcceptable()) {
// ServerSocketChannel 的 selectionKey
ServerSocketChannel server = (ServerSocketChannel) selectionKey.channel();
if(null == server){
return;
}
//接受到此通道套接字的连接,block here
SocketChannel client = server.accept();
// 配置为非阻塞
client.configureBlocking(false);
// 注册读到selector,等待读的selectionKey
client.register(selector, SelectionKey.OP_READ);
} else if (selectionKey.isReadable()) {
// SocketChannel 的 selectionKey
SocketChannel client = (SocketChannel) selectionKey.channel();
receiveBuffer.clear();
int count = client.read(receiveBuffer);
if (count > 0) {
String receiveText = new String( receiveBuffer.array(),0,count);
System.out.println(receiveText);
//注册写到selector,等待读的selectionKey
SelectionKey key = client.register(selector, SelectionKey.OP_WRITE);
//这里可以作为设计框架的扩展之处
key.attach(receiveText);
}
} else if (selectionKey.isWritable()) {
// SocketChannel selectionKey
SocketChannel client = (SocketChannel) selectionKey.channel();
//取出read 的 attachment
String request = (String) selectionKey.attachment();
String sendText="response--" + request;
sendBuffer.clear();
sendBuffer.put(sendText.getBytes());
sendBuffer.flip();
//输出到通道
client.write(sendBuffer);
System.out.println(sendText);
client.register(selector, SelectionKey.OP_READ);
}
}
/**
* [[@param](http://my.oschina.net/u/2303379)](http://my.oschina.net/u/2303379) args
* [[@throws](http://my.oschina.net/throws)](http://my.oschina.net/throws) IOException
*/
public static void main(String[] args) throws IOException {
int port = 10000;
NonBlockingIOServer server = new NonBlockingIOServer(port);
server.accept();
}
}
主要流程为:
而NIO基于Selector,当有感兴趣的事件发生时,就通知对应的事件处理器去处理事件,如果没有,则不处理。当socket有流可读或可写入socket时,操作系统会相应的通知引用程序进行处理,应用再将流读取到缓冲区或写入操作系统。所以使用一个线程做轮询就可以了。
Buffer,也是NIO的一个新特性,可以块状的读/写数据,效率得到极大的提高。
所以NIO提高了线程的利用率,减少系统在管理线程和线程上下文切换的开销。
AIO主要工作流程为:
public class AsynchronousIOServer {
private static Charset charset = Charset.forName("UTF-8");
public static void main(String[] args) {
int port = 10000;
int processors = Runtime.getRuntime().availableProcessors();
ExecutorService threadPool = Executors.newFixedThreadPool(processors);
try {
AsynchronousChannelGroup group = AsynchronousChannelGroup.withThreadPool(threadPool);
AsynchronousServerSocketChannel server = AsynchronousServerSocketChannel.open(group);
server.bind(new InetSocketAddress(port));
doAccept(server);
group.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);
} catch (IOException | InterruptedException e) {
e.printStackTrace();
System.out.println("close server");
System.exit(0);
}
}
private static void doAccept(AsynchronousServerSocketChannel server) {
server.accept(null, new CompletionHandler<AsynchronousSocketChannel, Void>() {
@Override
public void completed(AsynchronousSocketChannel client, Void attachment) {
server.accept(null, this);// accept next client connect
doRead(client, attachment);
}
@Override
public void failed(Throwable exc, Void attachment) {
exc.printStackTrace();
}
});
}
private static void doRead(AsynchronousSocketChannel client, Void attachment) {
ByteBuffer buffer = ByteBuffer.allocate(1024);
client.read(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer result, ByteBuffer attachment) {
if (result <= 0) {
try {
System.out.println("客户端断线:" + client.getRemoteAddress().toString());
attachment = null;
} catch (IOException e) {
e.printStackTrace();
}
return;
}
attachment.flip();
String req = charset.decode(attachment).toString();
attachment.compact();
client.read(attachment, attachment, this);// next client read
/** do service code **/
System.out.println(req);
ByteBuffer resBuffer = ByteBuffer.wrap(("response:" + req).getBytes());
doWrite(client, resBuffer, resBuffer);
}
@Override
public void failed(Throwable exc, ByteBuffer attachment) {
exc.printStackTrace();
}
});
}
private static <V> void doWrite(AsynchronousSocketChannel client, ByteBuffer resBuffer, ByteBuffer attachment) {
client.write(attachment, attachment, new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer result, ByteBuffer attachment) {
// TODO write success
if (result <= 0) {
try {
System.out.println("客户端断线:" + client.getRemoteAddress().toString());
attachment = null;
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Override
public void failed(Throwable exc, ByteBuffer attachment) {
exc.printStackTrace();
}
});
}
}
主要流程为:
BIO方式适用于连接数量小,连接时间短,计算密集,代码编写直观,程序直观简单易理解,JDK1.4之前。
NIO方式适用于连接数量大,连接时间短,比如Http服务器,并发局限于应用中,编程比较复杂,JDK1.4开始支持。
AIO方式使用于连接数量大,连接时间长,IO密集型,比如聊天服务器,充分调用OS参与并发操作,编程比较复杂,JDK7开始支持。
另外要清楚理解的:
?
原文:http://haoran-10.iteye.com/blog/2311266