理解Java NIO
对于Java NIO,总是看见别人写,使用Java NIO能够提高性能,比BIO的性能要好挺多,但是一直未能深入的研究,不太清楚NIO到底是怎么来提高性能的,Non-blocking到底体现在哪里。这几天搜索了一个,找到一些讲的比较好的文章,并实际写了一个小的程序来理解一下,对NIO有了更进一步的理解。
?
所参考查询的资料如下:
1. JAVA NIO 简介?
http://www.iteye.com/topic/834447
帖子讲解了NIO相关的知识,比较好的比较和总结了BIO和NIO的区别,指出了为什么NIO的性能比BIO要好,解答了我一直的疑问。帖子后面的回复非常有用,一定要看。
2. 使用Java NIO编写高性能的服务器?
http://tenyears.iteye.com/blog/40489
代码非常好,通过这个代码可以更好的理解NIO
?
下面根据2里面的代码写了一个程序,客户端想服务器请求下载文件,如果文件比较大,比较耗时,采用BIO的方式的话,如果起100个线程,只能100个client进行下载,第101个客户就得等待。而且CPU需要不断的切换来知道哪个线程中的IO读写可以进行了,开销比较大。使用NIO后,如果有IO读写到来,服务器就会得到相关的事件,开始进行读写,这样的开销比BIO要小很多,通过一个线程轮询事件就能完成。
?
看一下代码,里面加了一些log,有助于理解NIO
Server端的代码为:
package com.jyj.test.server;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.IOException;import java.net.InetSocketAddress;import java.nio.ByteBuffer;import java.nio.CharBuffer;import java.nio.channels.FileChannel;import java.nio.channels.SelectionKey;import java.nio.channels.Selector;import java.nio.channels.ServerSocketChannel;import java.nio.channels.SocketChannel;import java.nio.charset.Charset;import java.nio.charset.CharsetDecoder;import java.util.Iterator;import java.util.Set;public class NIOServer { private static final int BLOCK_SIZE = 4096; private Selector selector; private String file = "D:\\Learning\\Java\\HowTomcatWorksApps.zip"; private ByteBuffer buffer = ByteBuffer.allocate(BLOCK_SIZE); private CharsetDecoder charsetDecoder; public NIOServer(int port) throws IOException {selector = this.getSelector(port);Charset charset = Charset.forName("UTF-8");charsetDecoder = charset.newDecoder(); } private Selector getSelector(int port) throws IOException {ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();Selector selector = Selector.open();serverSocketChannel.socket().bind(new InetSocketAddress(port));serverSocketChannel.configureBlocking(false);serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);return selector; } public void listen() {while(true) { try {selector.select();Set<SelectionKey> selectionKeys = selector.selectedKeys();Iterator<SelectionKey> it = selectionKeys.iterator();System.out.println("keyset size : " + selectionKeys.size());while (it.hasNext()) { SelectionKey selectionKey = it.next(); it.remove(); handleKey(selectionKey);} } catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace(); } } } private void handleKey(SelectionKey key) throws IOException {if (key.isAcceptable()) { System.out.println("Accept"); ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel(); SocketChannel channel = serverSocketChannel.accept(); channel.configureBlocking(false); channel.register(selector, SelectionKey.OP_READ);} else if (key.isReadable()) { SocketChannel channel = (SocketChannel) key.channel(); int count = channel.read(buffer); if (count > 0) {buffer.flip();CharBuffer charBuffer = charsetDecoder.decode(buffer);String clientName = charBuffer.toString();System.out.println("Read From Client : " + clientName);SelectionKey selectionKey = channel.register(selector, SelectionKey.OP_WRITE);selectionKey.attach(new HandleClient(clientName)); } else {channel.close(); } buffer.clear();} else if (key.isWritable()) { SocketChannel channel = (SocketChannel) key.channel(); HandleClient handleClient = (HandleClient) key.attachment(); ByteBuffer buffer = handleClient.readBlock(); System.out.println("Write to client : " + handleClient.getClentName()); if (buffer != null) {channel.write(buffer); } else {handleClient.close();channel.close(); }} } private class HandleClient {private FileChannel fileChannel;private ByteBuffer byteBuffer;private String clientName;public HandleClient(String clientName) throws FileNotFoundException { fileChannel = new FileInputStream(file).getChannel(); byteBuffer = ByteBuffer.allocate(BLOCK_SIZE); this.clientName = clientName;}public ByteBuffer readBlock() { try {byteBuffer.clear();int count = fileChannel.read(byteBuffer);byteBuffer.flip();if (count < 0) { return null;} } catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace(); } return byteBuffer;}public void close() { try {fileChannel.close(); } catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace(); }}public String getClentName() { return clientName;} } public static void main(String [] args) {int port = 12345;try { NIOServer server = new NIOServer(port); System.out.println("Listening on : " + port); while (true) {server.listen(); }} catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace();} }}
?
client端的代码为:
package com.jyj.test.client;import java.io.IOException;import java.net.InetSocketAddress;import java.nio.ByteBuffer;import java.nio.CharBuffer;import java.nio.channels.SelectionKey;import java.nio.channels.Selector;import java.nio.channels.SocketChannel;import java.nio.charset.Charset;import java.nio.charset.CharsetEncoder;import java.util.Iterator;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;public class NIOClient { static int SIZE = 10; static InetSocketAddress address = new InetSocketAddress("localhost", 12345); static CharsetEncoder encoder = Charset.forName("UTF-8").newEncoder(); static class DownloadFile implements Runnable {int index;public DownloadFile(int index) { this.index = index;}@Overridepublic void run() { long start = System.currentTimeMillis(); try {SocketChannel client = SocketChannel.open();client.configureBlocking(false);Selector selector = Selector.open();client.register(selector, SelectionKey.OP_CONNECT);client.connect(address);ByteBuffer buffer = ByteBuffer.allocate(8 * 1024);int total = 0;while(true) { boolean isExit = false; selector.select(); Iterator<SelectionKey> it = selector.selectedKeys().iterator(); while (it.hasNext()) {SelectionKey selectionKey = (SelectionKey) it.next();it.remove();if (selectionKey.isConnectable()) { SocketChannel channel = (SocketChannel) selectionKey.channel(); if (channel.isConnectionPending()) {channel.finishConnect(); } channel.write(encoder.encode(CharBuffer.wrap("Hello From " + index))); channel.register(selector, SelectionKey.OP_READ);} else if (selectionKey.isReadable()) { SocketChannel channel = (SocketChannel) selectionKey.channel(); int count = channel.read(buffer); if (count > 0) {total += count;buffer.clear(); } else {channel.close();isExit = true;break; }} } if (isExit) {break; }}double last = (System.currentTimeMillis() - start) * 1.0 / 1000;System.out.println("Thread " + index + " downloaded " + total + " bytes in " + last + "seconds."); } catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace(); } } } public static void main(String [] args) {ExecutorService pool = Executors.newFixedThreadPool(SIZE);for (int i = 0; i < SIZE; i++) { pool.execute(new DownloadFile(i));}pool.shutdown(); }}?
server端的输出结果如下:
Listening on : 12345keyset size : 1Acceptkeyset size : 1Acceptkeyset size : 1Read From Client : Hello From 8keyset size : 3AcceptRead From Client : Hello From 2Write to client : Hello From 8keyset size : 3Read From Client : Hello From 5Write to client : Hello From 2Write to client : Hello From 8keyset size : 3Write to client : Hello From 5Write to client : Hello From 2Write to client : Hello From 8。。。。。。。。。。。。。。。keyset size : 10Write to client : Hello From 7Write to client : Hello From 1Write to client : Hello From 5Write to client : Hello From 3Write to client : Hello From 9Write to client : Hello From 2Read From Client : Hello From 0Write to client : Hello From 6Write to client : Hello From 8Write to client : Hello From 4keyset size : 10Write to client : Hello From 7Write to client : Hello From 1Write to client : Hello From 5Write to client : Hello From 3Write to client : Hello From 9Write to client : Hello From 0Write to client : Hello From 2Write to client : Hello From 6Write to client : Hello From 8Write to client : Hello From 4。。。。。。。。。。。。。。。keyset size : 3Write to client : Hello From 0Write to client : Hello From 6Write to client : Hello From 4keyset size : 3Write to client : Hello From 0Write to client : Hello From 6Write to client : Hello From 4keyset size : 3Write to client : Hello From 0Write to client : Hello From 6Write to client : Hello From 4keyset size : 3Write to client : Hello From 0Write to client : Hello From 6Write to client : Hello From 4keyset size : 3Write to client : Hello From 0Write to client : Hello From 6Write to client : Hello From 4keyset size : 2Write to client : Hello From 6Write to client : Hello From 4keyset size : 2Write to client : Hello From 6Write to client : Hello From 4keyset size : 2Write to client : Hello From 6Write to client : Hello From 4keyset size : 1Write to client : Hello From 4keyset size : 1Write to client : Hello From 4keyset size : 1Write to client : Hello From 4keyset size : 1Write to client : Hello From 4keyset size : 1Write to client : Hello From 4keyset size : 1Write to client : Hello From 4
?
client端的输出结果为:
Thread 5 downloaded 3844845 bytes in 1.938seconds.Thread 2 downloaded 3844845 bytes in 1.953seconds.Thread 1 downloaded 3844845 bytes in 1.953seconds.Thread 8 downloaded 3844845 bytes in 1.922seconds.Thread 9 downloaded 3844845 bytes in 1.922seconds.Thread 7 downloaded 3844845 bytes in 1.953seconds.Thread 3 downloaded 3844845 bytes in 1.969seconds.Thread 0 downloaded 3844845 bytes in 1.969seconds.Thread 6 downloaded 3844845 bytes in 1.953seconds.Thread 4 downloaded 3844845 bytes in 1.953seconds.
?
最近建议找一下linux或者windows的socket编程方面的书看一下,能够更好的理解IO方面的知识,包括Java 7中的AIO。