NIO:同步非阻塞IO
来源:BIO是同步阻塞IO操作,当线程在处理任务时,另一方会阻塞着等待该线程的执行完毕,为了提高效率,,JDK1.4后,引入NIO来提升数据的通讯性能
NIO中采用Reactor设计模式,注册的汇集点为Selector,NIO有三个主要组成部分:Channel(通道)、Buffer(缓冲区)、Selector(选择器)
NIO采用了轮询的方式来观察事件是否执行完毕,如:A让B打印某个文件,BIO会一直等待着B返回,期间自己不做其他事情,而NIO则会不断的询问B是否完成,未完成则处理自己的时,直至B完成
Channel(通道):Channel是一个对象,可以通过它读取和写入数据
Selector(对象选择器): Selector是一个对象,它可以注册到很多个Channel上,监听各个Channel上发生的事件,并且能够根据事件情况决定Channel读写
代码实现:(此实现参考网络上可用的例子)
NIO客户端实现:
packagecom.learn.nio.client;importcom.study.info.HostInfo;importcom.study.util.InputUtil;importjava.net.InetSocketAddress;importjava.nio.ByteBuffer;importjava.nio.channels.SocketChannel;public classNIOEchoClient {public static void main(String[] args) throwsException{
SocketChannel clientChannel=SocketChannel.open();
clientChannel.connect(newInetSocketAddress(HostInfo.HOST_NAME,HostInfo.PORT));
ByteBuffer buffer= ByteBuffer.allocate(50);boolean flag = true;while(flag){
buffer.clear();
String input= InputUtil.getString("请输入待发送的信息:").trim();
buffer.put(input.getBytes());//将数据存入缓冲区
buffer.flip(); //重置缓冲区
clientChannel.write(buffer); //发送数据
buffer.clear();int read =clientChannel.read(buffer);
buffer.flip();
System.err.print(new String(buffer.array(), 0, read));if("byebye".equalsIgnoreCase(input)){
flag= false;
}
}
clientChannel.close();
}
}
NIO服务端实现:
packagecom.learn.nio.server;importcom.study.info.HostInfo;importjava.net.InetSocketAddress;importjava.nio.ByteBuffer;importjava.nio.channels.SelectionKey;importjava.nio.channels.Selector;importjava.nio.channels.ServerSocketChannel;importjava.nio.channels.SocketChannel;importjava.util.Iterator;importjava.util.Set;importjava.util.concurrent.ExecutorService;importjava.util.concurrent.Executors;public classNIOEchoServer {private static class EchoClientHandle implementsRunnable {//客户端
privateSocketChannel clientChannel;//循环结束标记
private boolean flag = true;publicEchoClientHandle(SocketChannel clientChannel){this.clientChannel =clientChannel;
}
@Overridepublic voidrun() {
ByteBuffer byteBuffer= ByteBuffer.allocate(50);try{while (this.flag){
byteBuffer.clear();int read = this.clientChannel.read(byteBuffer);
String msg= new String(byteBuffer.array(), 0, read).trim();
String outMsg= "【Echo】" + msg + "\n"; //回应信息
if("byebve".equals(msg)){
outMsg= "会话结束,下次再见!";this.flag = false;
}
byteBuffer.clear();
byteBuffer.put(outMsg.getBytes());//回传信息放入缓冲区
byteBuffer.flip();this.clientChannel.write(byteBuffer);//回传信息
}
}catch(Exception e){
e.printStackTrace();
}
}
}public static void main(String[] args) throwsException{//为了性能问题及响应时间,设置固定大小的线程池
ExecutorService executorService = Executors.newFixedThreadPool(10);//NIO基于Channel控制,所以有Selector管理所有的Channel
ServerSocketChannel serverSocketChannel =ServerSocketChannel.open();//设置为非阻塞模式
serverSocketChannel.configureBlocking(false);//设置监听端口
serverSocketChannel.bind(newInetSocketAddress(HostInfo.PORT));//设置Selector管理所有Channel
Selector selector =Selector.open();//注册并设置连接时处理
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("服务启动成功,监听端口为:" +HostInfo.PORT);//NIO使用轮询,当有请求连接时,则启动一个线程
int keySelect = 0;while ((keySelect = selector.select()) > 0){
Set selectionKeys =selector.selectedKeys();
Iterator iterator =selectionKeys.iterator();while(iterator.hasNext()){
SelectionKey next=iterator.next();if(next.isAcceptable()){ //如果是连接的
SocketChannel accept =serverSocketChannel.accept();if(accept != null){
executorService.submit(newEchoClientHandle(accept));
}
iterator.remove();
}
}
}
executorService.shutdown();
serverSocketChannel.close();
}
}
工具类:
packagecom.study.util;importjava.io.BufferedReader;importjava.io.IOException;importjava.io.InputStreamReader;public classInputUtil {private static final BufferedReader KEYBOARD_INPUT = new BufferedReader(newInputStreamReader(System.in));privateInputUtil(){
}public staticString getString(String prompt){boolean flag = true; //数据接受标记
String str = null;while(flag){
System.out.println(prompt);try{
str= KEYBOARD_INPUT.readLine(); //读取一行数据
if(str == null || "".equals(str)){
System.out.println("数据输入错误,不允许为空!");
}else{
flag= false;
}
}catch(IOException e) {
e.printStackTrace();
}
}returnstr;
}
}
packagecom.study.info;publiccalss HostInfo {public static final String HOST_NAME = "localhost";public static final int PORT = 9999;
}