问题:系统运行瓶颈,I/O读写,因为打开一个I/O通道以后,read()将一直等待在端口一边读取字节内容,如果没有内容进来,read()也是傻傻的等,这会影响我们程序继续做其他事情,那么改进做法就是开设线程,让线程去等待,但是这样做也是相当耗费资源的。
Java NIO解决方案:采用非阻塞技术,采取Reactor模式或者观察者模式,等到内容进来再自动通知,而不必死等,使I/O读写通畅,不堵塞了。
具体原理描述:NIO有一个主要的类selector,它类似一个观察者,只要我们把需要探知的socketChannel告知selector,我们可以安心干其他事情,当有事件发生时,它会消息通知我们,传回一组selectionKey,根据这个key可以获得刚刚注册的socketChannel,然后就可以从这个Channel中读取数据。
Selector的内部原理:实际上是对所有channel做一个轮询访问,一旦发现有事件,比如数据来了,它就会通知报告,交出一把钥匙。
以下是测试代码
/**
* Start server
*
* @throws IOException
*/
public void startServer() throws IOException {
int channels = 0;
int nKeys = 0;
// int currentSelector = 0;
// Use selector
Selector selector = Selector.open();
ServerSocketChannel ssc = ServerSocketChannel.open();
InetSocketAddress address = new InetSocketAddress(InetAddress.getLocalHost(), 9000);
// Bind address
ssc.socket().bind(address);
// Non-blocking model
ssc.configureBlocking(false);
// Register
SelectionKey sk = ssc.register(selector, SelectionKey.OP_ACCEPT);
printKeyInfo(sk);
// For each
while (true) {
debug("Starting select...");
// Some thing happened. return a set of keys which channels are ready for I/O
nKeys = selector.select();
if(nKeys > 0) {
debug("Number of keys after select operation: " + nKeys);
//Selector传回一组SelectionKeys
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> i = selectedKeys.iterator();
while(i.hasNext()) {
sk = (SelectionKey) i.next();
printKeyInfo(sk);
debug("Nr Keys in selector: " +selector.keys().size());
// Remove
i.remove();
if(sk.isAcceptable()) {
Socket socket = ((ServerSocketChannel)sk.channel()).accept().socket();
SocketChannel sc = socket.getChannel();
sc.configureBlocking(false);
sc.register(selector, SelectionKey.OP_READ |SelectionKey.OP_WRITE);
System.out.println(++channels);
} else {
debug("Channel not acceptable");
}
}
} else {
debug("Select finished without any keys.");
}
}
}
/**
* Debug
*
* @param str
*/
private static void debug(String str) {
System.out.println("NIO Test: " + str);
}
/**
* Print key info
*
* @param sk
*/
private static void printKeyInfo(SelectionKey sk) {
StringBuffer sb = new StringBuffer();
sb.append("Att: " + (sk.attachment() == null ? "no" : "yes"));
sb.append(", Read: " + sk.isReadable());
sb.append(", Acpt: " + sk.isAcceptable());
sb.append(", Cnct: " + sk.isConnectable());
sb.append(", Wrt: " + sk.isWritable());
sb.append(", Valid: " + sk.isValid());
sb.append(", Ops: " + sk.interestOps());
debug(sb.toString());
}