博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
java的NIO概述
阅读量:6221 次
发布时间:2019-06-21

本文共 6288 字,大约阅读时间需要 20 分钟。

hot3.png

       概述

 java中io输入输出流是阻塞的,如BufferedReader的readLine(),InputStrean的read()方法。当程序没有读到有效数据,程序将在此处阻塞。比较典型的是,在用socket传递数据,如果发送数据的一方,没有关闭流,接收数据的一方,接收数据后将一直阻塞。面向流的输入输出,最底层一次只能处理一个字节,因此效率不高。

    jdk1.4提供了新的IO即NIO。

    NIO中有三个重要的特性。

    Channel,通道。模拟传统的输入输出流,既可以做输入又可以做输出。

    Buffer,缓冲。本质是一个数组,发送到Channel的所有对象都必须先放到Buffer中。读取数据也先读到Buffer。

    Selector。selector允许单线程处理多个Channel,可监控多个服务

    Buffer简介

    buffer中有三个重要概念:容量(capactiy)、界限(limit)和位置(position,带读取数据的下标)。

    这些值满足如下关系:0<=mark<=position<=limit<=capacity

    Buffer中包含两个重要的方法flip和clear,flip为从Buffer中取出数据做准备,clear则向Buffer中装入数据做准备。

    当Buffer中装入数据结束后,调用flip方法,该方法将limit设置为position所在的位置,将position设为0,这样Buffer中读数据总是从0开始,做好输出准备。当Buffer输出数据结束,Buffer调用clear方法,clear方法不是清空Buffer的数据,它仅仅将poition置为0,将limit置为capacity,这样为再次向Buffer装入数据做准备,此时原先的数据没有清除,新写的数据会替换原先的数据。

    Channel简介

    所有的Channel都不应该通过构造器来直接创建,而是通过InputStream、OutputStream的getChannel方法来返回对应的Channel。

    Channel中最常用的三类方法是map、read和write,其中map方法用于将Channel对应的部分或全部数据映射为ByteBuffer;而read或write方法用于向Buffer读取写入数据。

static void channelTest() throws Exception{		FileInputStream is = new FileInputStream(new File("G:/百无聊赖.txt"));		FileOutputStream out = new FileOutputStream(new File("G:/ttt.txt"));		FileChannel readChannel = is.getChannel();		FileChannel writechannel = out.getChannel();		ByteBuffer buffer = ByteBuffer.allocate(1024);		while(readChannel.read(buffer)!=-1){			buffer.flip();//准备取出数据			writechannel.write(buffer);			buffer.clear();//准备装入数据		}		writechannel.close();		readChannel.close();		out.close();		is.close();	}

因为jdk对之前的IO采用nio的机制重新实现了,所以直接使用nio对数据进行读写并不常用。使用channel的map方法,进行内存映射,可以快速的读写数据,但是这种方式不安全,仅使用与读数据。NIO最常用的功能应该是它的Selector。

Java NIO的选择器允许一个单独的线程来监视多个输入通道,你可以注册多个通道使用一个选择器,然后使用一个单独的线程来“选择”通道:这些通道里已经有可以处理的输入,或者选择已准备写入的通道。这种选择机制,使得一个单独的线程很容易来管理多个通道。 

如下两篇博文对NIO包括selector的介绍都不错

 

下面构建一个小例子对比下NIO和IO服务器

服务端代码:

package com.base.nio;import java.io.BufferedReader;import java.io.BufferedWriter;import java.io.IOException;import java.io.InputStreamReader;import java.io.OutputStreamWriter;import java.net.InetSocketAddress;import java.net.ServerSocket;import java.net.Socket;import java.nio.ByteBuffer;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;public class Server {	public static void main(String[] args) throws Exception{//		nioServer();		socketServer();	}	public static void nioServer() throws Exception{		int port=9999;		Selector selector=Selector.open();		ServerSocketChannel ssc=ServerSocketChannel.open();		ServerSocket serverSocket=ssc.socket();		serverSocket.bind(new InetSocketAddress(port));		System.out.println("Server listen on port: "+port);		ssc.configureBlocking(false);		ssc.register(selector, SelectionKey.OP_ACCEPT);		while(true){			int nKeys=selector.select();			if(nKeys>0){				for (SelectionKey key : selector.selectedKeys()) {					if(key.isAcceptable()){						ServerSocketChannel server=(ServerSocketChannel) key.channel();						SocketChannel sc=server.accept();						if(sc==null){							continue;						}						sc.configureBlocking(false);						sc.register(selector, SelectionKey.OP_READ);					}					else if(key.isReadable()){						ByteBuffer buffer=ByteBuffer.allocate(1024);						SocketChannel sc=(SocketChannel) key.channel();						int readBytes=0;						readBytes = sc.read(buffer);//如果客户端传来的数据长度超过1024,会丢失报文。						buffer.flip();						if(readBytes>0){							String message =Charset.forName("UTF-8").decode(buffer).toString();							System.out.println("Message from client: "+ message);							if("quit".equalsIgnoreCase(message.trim())){								sc.close();								selector.close();								System.out.println("Server has been shutdown!");								System.exit(0);							}							String outMessage="Server response:"+message;							sc.write(Charset.forName("UTF-8").encode(outMessage));						}					}				}				selector.selectedKeys().clear();			}		}	}	public static void socketServer() throws IOException, InterruptedException, Exception {		ServerSocket ss = new ServerSocket(9999);		while(true){			Socket socket = ss.accept();			BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));			String getMsg = br.readLine();			if("quit".equalsIgnoreCase(getMsg.trim())){				ss.close();				System.out.println("Server has been shutdown!");				System.exit(0);			}			System.out.println(getMsg);			BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));			bw.write("Hello Client");			bw.newLine();			bw.flush();			Thread.sleep(10000);			Client.close(socket, null, bw, br);		}	}}

客户端代码:

package com.base.nio;import java.io.BufferedReader;import java.io.BufferedWriter;import java.io.InputStreamReader;import java.io.OutputStream;import java.io.OutputStreamWriter;import java.net.Socket;public class Client implements Runnable{	public static void main(String[] args) throws Exception{		for(int i=0;i<10;i++){			Client c = new Client();			Thread thread = new Thread(c);			thread.start();		}	}	@Override	public void run() {		Socket socket = null;		OutputStream out = null;		BufferedWriter bw = null;		BufferedReader br = null;		try {			socket = new Socket("127.0.0.1", 9999);			out = socket.getOutputStream();			bw = new BufferedWriter(new OutputStreamWriter(out));			bw.write("Hello Server");			bw.newLine();			bw.flush();			Thread.sleep(10000);			br = new BufferedReader(new InputStreamReader(socket.getInputStream()));			String retMsg = br.readLine();			System.out.println(retMsg+Thread.currentThread().getName());		} catch (Exception e) {			e.printStackTrace();		} finally{			try {				Client.close(socket, out, bw,br);			} catch (Exception e) {				e.printStackTrace();			}		}	}		public static void close(Socket socket, OutputStream out, BufferedWriter bw,BufferedReader br) throws Exception{		if(br!=null)			br.close();		if(bw!=null)			bw.close();		if(out!=null)			out.close();		if(socket!=null)			socket.close();	}}

通过运行分析,发现nio是非阻塞的,不用等每一个客户端的连接请求完成,就可以进行下一个连接。而传统的socket会等待客户端的每个请求完成,才去连接下个客户端请求。

nio是通过while(true)进行轮询,来不断监听客户端的请求状态。而socket是通过阻塞的方式来等待客户端的请求状态。

感觉NIO最大的优势就在于它的非阻塞,减少了等待客户的响应。如果使用socket要想达到同样的效果,必须使用线程池,使用线程池就相对耗资源些。

至于监听多个chanel,也就是同时监听多个端口。比如以上例子,9999用于监听客户端会话。如果服务端还要一个8888端口,来控制其他服务,比如关闭服务等等。传统的socket也必须用多线程来实现。

但是感觉NIO采用这种轮的方式,不是很优雅。可能我了解的不多吧。

转载于:https://my.oschina.net/everyDay111/blog/552562

你可能感兴趣的文章
Golang入门教程(二)Ubuntu16.04下安装golang(实例:Golang 定时任务管理器)
查看>>
Maven - 项目结构
查看>>
大话系统之权限控制
查看>>
如何在CRM系统中集成ActiveReports最终报表设计器
查看>>
10.2.0.4 11.1.0.7UNDO可能异常增大的BUG
查看>>
大学里的几件趣事(一)
查看>>
C#流水号生成汇总(四)
查看>>
Linux 内存使用方法详细解析
查看>>
工程师文化,是一种内心的欲望与恐惧的表达。对创造的欲望,对世界的恐惧。因为欲望而创造,因为恐惧而改造。创造世界,改造世界。(转)...
查看>>
直击游戏行业音视频应用——12月2日livevideostack Meet成都沙龙
查看>>
[20170703]ora-12516 ora-12514 rac.txt
查看>>
SQL 2008执行语句遇到内存不足(1)——error 701
查看>>
Sphinx
查看>>
【Spring】Spring常用配置-事件(Application Event)
查看>>
Git是个好工具(转)
查看>>
ffmpeg处理RTMP流媒体的命令大全
查看>>
OpenCV轮廓检测,计算物体旋转角度
查看>>
VSS
查看>>
【转载】CodePipeline联动容器的DevOps实践
查看>>
Al x 量化:智能投顾如何解决金融机构财富管理业务的痛点?
查看>>