Java NIO介绍及使用,javanio介绍
Java NIO介绍及使用,javanio介绍
1.什么是Java NIO
大家都知道Java BIO,其全称是java blocking IO,相对的Java NIO 全称为java non-blocking IO。顾名思义,java nio 是一种非阻塞IO。
下面是百度百科对Java NIO的介绍:
java.nio全称java non-blocking IO,是指jdk1.4 及以上版本里提供的新api(New IO) ,为所有的原始类型(boolean类型除外)提供缓存支持的数据容器,使用它可以提供非阻塞式的高伸缩性网络。
2 . NIO和BIO的区别
其本质就是阻塞与非阻塞的区别。
Java IO 的各种流都是阻塞的,这意味着,当一个线程进行流处理(如read()和write())时,无论是否有数据,该线程会一直被阻塞,直到流通信结束。在此期间线程不能干其他的事情,就算当前没有数据,线程依然保持等待状态。这样无疑会浪费大量的资源。而在NIO的非阻塞模式下,线程发送数据与接收数据都是通过“通道”进行的,线程只需要去询问通道是否有数据需要处理,有则处理,无则立即返回不会进行等待。线程通常将非阻塞IO的空闲时间用于处理其他通道上的IO事件,使用一个单独的线程就可以管理多个输入和输出通道。
那么NIO是怎么实现非阻塞的呢?其实原理很简单,NIO是面向块的,先把数据搬运过来,存放到一个缓冲区中,线程过一段时间来缓冲区看看,有没有数据,这个样线程就不需要始终关注IO了。
(BIO为同步阻塞模型,NIO为同步非阻塞模型。NIO没有实现异步,在JDK1.7后,升级了NIO库包,支持异步非阻塞通信模型,即AIO)
3 . Java NIO的几个相关概念
学习NIO,必须了解几个概念:
(1) . Buffer
Buffer是一个对象,它用来存放即将发送的数据和即将到来的数据。Buffer是NIO核心思想,它与普通流IO的区别是,普通流IO直接把数据写入或读取到Stream对象中,而NIO是先把读写数据交给Buffer,后在用流处理的。Buffer实际上就是一个数组,通常是字节数组,这个数组提供了访问数据的读写等操作属性,如位置,容量,上限等概念。
Buffer类型:
- ByteBuffer
- CharBuffer
- DoubleBuffer
- FloatBuffer
- IntBuffer
- LongBuffer
- ShortBuffer
这写Buffer覆盖了你能通过IO发送的基本数据类型:byte , short , int , long , double , char 。
(2) .Channel
Channel(通道),与Stream(流)的不同之处在于通道是双向的,流只能在一个方向上操作(一个流必须是InputStream或者OutputStream的子类),而通道可以用于读,写或者二者同事进行,最关键的是可以和多路复用器结合起来,提供状态位,多路复用器可识别Channel所处的状态。
通道分两大类:用于网络读写的SelectableChannel,和用于文件操作的FileChannel。
(3) . Selector
NIO的编程基础,Selector提供选择已经就绪的任务的能力。简单说,就是Selector会不断轮询注册在Selector上的通道(Channel),如果这个通道发生了读写操作,这个通道就会处于就绪状态,会被Selector察觉到,然后通过SelectionKey可以取出就绪的Channel集合,从而进行IO操作。
一个Selector可以负责成千上万的通道,没有上限。这也是JDK使用了epoll代替传统的Select实现,获得连接句柄没有限制。意味着我们只需要一个线程负责Selector的轮询,就可以接入成百上千的客户端,这是JDK NIO库的巨大进步。
(4) . 原理图
图片来至于:
http://ifeve.com/overview/
4 . 使用例子
Server.class
public class Server implements Runnable
{
private Selector selector;
private ByteBuffer buffer = ByteBuffer.allocate(1024); //?
public Server(int port){
try {
selector = Selector.open();
ServerSocketChannel ssc = ServerSocketChannel.open();
//设置服务器为非阻塞方式
ssc.configureBlocking(false);
ssc.bind(new InetSocketAddress(port));
//把服务器通道注册到多路复用选择器上,并监听阻塞状态
ssc.register(selector,SelectionKey.OP_ACCEPT);
System.out.println("Server start whit port : "+port);
} catch (IOException e) {
e.printStackTrace();
}
}
public void run(){
while(true){
try {
//会这这里处理事件,也是阻塞的,事件包括客户端连接,客户端发送数据到来,以及客户端断开连接等等
//若没有事件发生,也会阻塞
selector.select();
//System.out.println("阻塞在这");
//返回所有已经注册到多路复用选择器的通道的SelectionKey
Iterator<SelectionKey> keys = selector.selectedKeys().iterator();
//遍历keys
while(keys.hasNext()){
SelectionKey key = keys.next();
//下一个key,就像数组访问的i++
keys.remove();
if(key.isValid()){ //判断key是否有效
if(key.isAcceptable()){ //请求连接事件
accept(key); //处理新客户的连接
}
if(key.isReadable()){ //有数据到来
read(key);
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 处理客户端连接
* 服务器为每个客户端生成一个Channel
* Channel与客户端对接
* Channel绑定到Selector上
* **/
private void accept(SelectionKey key){
try {
//获取之前注册的SocketChannel通道
ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
//执行阻塞方法,Channel和客户端对接
SocketChannel sc = ssc.accept();
//设置模式为非阻塞
sc.configureBlocking(false);
sc.register(selector,SelectionKey.OP_READ);
}catch(Exception e){
e.printStackTrace();
}
}
private void read(SelectionKey key){
try {
//清空缓冲区的旧数据
buffer.clear();
SocketChannel sc = (SocketChannel) key.channel();
int count = sc.read(buffer);
if(count == -1){
key.channel().close();
key.cancel();
return;
}
//读取到了数据,将buffer的position复位到0
buffer.flip();
byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes);
String body = new String(bytes).trim();
System.out.println("Server:"+body);
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main( String[] args )
{
new Thread(new Server(8379)).start();
}
}
Client.class
public class Client {
public static void main(String[] args){
InetSocketAddress address = new InetSocketAddress("127.0.0.1",8379);
SocketChannel sc =null;
ByteBuffer buffer = ByteBuffer.allocate(1024);
try{
sc = SocketChannel.open();
sc.connect(address);
while(true){
byte[] bytes = new byte[1024];
System.in.read(bytes);
buffer.put(bytes);
buffer.flip();
sc.write(buffer);
buffer.clear();
}
}catch(IOException e){
e.printStackTrace();
}finally {
if(sc!=null){
try {
sc.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
}
}
从服务器角度上看:
从客户端角度上看:
5 . 基于Java NIO的框架
推荐大家使用成熟的NIO框架,如Netty,MINA等。解决了很多NIO的陷阱,并屏蔽了操作系统的差异,有较好的性能和编程模型。
6.引用
https://blog.csdn.net/haoyuyang/article/details/53231585
http://ifeve.com/overview/
相关文章
- 暂无相关文章
用户点评