JavaIO和JavaNIO
BIO和NIO
BIO在之前的服务器处理模型中,在调用ServerSocket.accept()方法时,会一直阻塞到有客户端连接才会返回,每个客户端连接过来后,服务端都会accept一个新连接,接着启动一个线程去处理该客户端的请求。在这个新的线程中,也会在read()方法中阻塞,直到读取完数据,处理完成后销毁该处理线程。
这样会有什么问题呢?
当客户端并发访问增加后,服务端线程个数膨胀,频繁出现由于IO阻塞导致挂起的线程,系统性能将急剧下降,容易发生线程堆栈溢出、创建新线程失败等问题。
阻塞导致大量线程资源被浪费;阻塞可导致大量的上下文切换,很多切换其实是无意义的
Java自1.4以后,加入了新IO特性NIO,NIO带来了non-blocking特性。
那么NIO是如何帮助我们解决这种问题的呢(反应器设计模式)?
1). 由一个专门的线程来处理所有的 IO 事件,并负责分发。
2). 事件驱动机制:事件到的时候触发,而不是同步的去监视事件。
3). 线程通讯:线程之间通过 wait,notify 等方式通讯。保证每次上下文切换都是有意义的。减少无谓的线程切换。
服务端和客户端各自维护一个管理通道的对象,我们称之为selector,该对象能检测一个或多个通道 (channel) 上的事件。我们以服务端为例,如果服务端的selector上注册了读事件,某时刻客户端给服务端发送了一些数据,NIO的服务端会在selector中添加一个读事件。服务端的处理线程会轮询地访问selector,如果访问selector时发现有感兴趣的事件到达,则处理这些事件,如果没有感兴趣的事件到达,则处理线程会一直阻塞直到感兴趣的事件到达为止。
1、IO的例子
/* 字节IO */ public void byteIO() throws FileNotFoundException, IOException { FileInputStream fin = new FileInputStream(new File( "D:\\test\\byteio_in.txt")); FileOutputStream fout = new FileOutputStream(new File( "D:\\test\\byteio_out.txt")); int c = -1; while ((c = fin.read()) != -1) { fout.write(c); } fin.close(); fout.close(); } /* 字符IO */ public void charIO() throws FileNotFoundException, IOException { FileReader reader = new FileReader(new File("D:\\test\\chario_in.txt", "")); FileWriter writer = new FileWriter(new File("D:\\test\\chario_out.txt")); char[] charArr = new char[512]; while (reader.read(charArr) != -1) { writer.write(charArr); } reader.close(); writer.close(); } /* bufferIO */ public void bufferIO() throws FileNotFoundException, IOException { BufferedInputStream bufferReader = new BufferedInputStream( new FileInputStream("D:\\test\\bufferio_in.txt")); BufferedOutputStream bufferWriter = new BufferedOutputStream( new FileOutputStream("D:\\test\\bufferio_out.txt")); int c = -1; while ((c = bufferReader.read()) != -1) { bufferWriter.write(c); } bufferReader.close(); bufferWriter.close(); }
2、NIO的例子
/* NIO */ public void NIO() throws FileNotFoundException, IOException { FileInputStream fin = new FileInputStream("D:\\test\\nio_in.txt"); FileOutputStream fout = new FileOutputStream("D:\\test\\nio_out.txt"); FileChannel finChannel = fin.getChannel(); FileChannel foutChannel = fout.getChannel(); ByteBuffer buffer = ByteBuffer.allocate(512); while (finChannel.read(buffer) != 1)//读到缓存 { buffer.flip();//指针跳到缓存头 foutChannel.write(buffer); buffer.clear();//重置缓冲区 } fin.close(); fout.close(); }
3、NIO实现非阻塞Server服务
下面是一个NIO实现Server的例子
public class MultiPortEcho { private int ports[]; private ByteBuffer echoBuffer = ByteBuffer.allocate(1024); public MultiPortEcho(int ports[]) throws IOException { this.ports = ports; go(); } private void go() throws IOException { // Create a new selector Selector selector = Selector.open(); // Open a listener on each port, and register each one with the selector for (int i = 0; i < ports.length; ++i) { ServerSocketChannel ssc = ServerSocketChannel.open(); ssc.configureBlocking(false); ServerSocket ss = ssc.socket(); InetSocketAddress address = new InetSocketAddress(ports[i]); ss.bind(address); SelectionKey key = ssc.register(selector, SelectionKey.OP_ACCEPT); System.out.println("Going to listen on " + ports[i]); } while (true) { int num = selector.select(); Set selectedKeys = selector.selectedKeys(); Iterator it = selectedKeys.iterator(); while (it.hasNext()) { SelectionKey key = (SelectionKey) it.next(); if ((key.readyOps() & SelectionKey.OP_ACCEPT) == SelectionKey.OP_ACCEPT) { // Accept the new connection ServerSocketChannel ssc = (ServerSocketChannel) key.channel(); SocketChannel sc = ssc.accept(); sc.configureBlocking(false); // Add the new connection to the selector SelectionKey newKey = sc.register(selector,SelectionKey.OP_READ); it.remove(); System.out.println("Got connection from " + sc); } else if ((key.readyOps() & SelectionKey.OP_READ) == SelectionKey.OP_READ) { // Read the data SocketChannel sc = (SocketChannel) key.channel(); // Echo data int bytesEchoed = 0; while (true) { echoBuffer.clear(); int r = sc.read(echoBuffer); if (r <= 0) { break; } echoBuffer.flip(); sc.write(echoBuffer); bytesEchoed += r; } System.out.println("Echoed " + bytesEchoed + " from " + sc); it.remove(); } } } } static public void main(String args[]) throws Exception { int ports[] = {1234,6765,7987}; for (int i = 0; i < args.length; ++i) { ports[i] = Integer.parseInt(args[i]); } new MultiPortEcho(ports); } }
参考资料:
http://www.ibm.com/developerworks/cn/education/java/j-nio/j-nio.html#ibm-pcon
http://weixiaolu.iteye.com/blog/1479656