java nio是从java 1.4版本开始引入的。Java NIO和IO之间最大的区别:IO是面向流的,NIO是面向缓冲区的。nio核心组件:channel buffer selector三大组件
java io适用场景(原文链接:http://tutorials.jenkov.com/java-nio/overview.html):
1、并发量较小;
2、单连接一次发送数据包较大(要求内存足够,此读写线程阻塞);
java nio适用场景:
1、并发量较大;
2、单连接一次发送数据包不大(数据包较大,读数据处理复杂度高),如QQ聊天;
channel 类似于流,但流是意向的,通道是双向的
//从文件中读写数据 FileChannel fc = null; //能通过TCP读写网络中的数据 SocketChannel sc = null; //能通过UDP读写网络中的数据 DatagramChannel dc = null; //可以监听新进来的TCP连接,像Web服务器那样。对每一个新进来的连接都会创建一个SocketChannel。 ServerSocketChannel ssc = null;
一 通道Channel
Java NIO的通道类似流,但又有些不同:
-
- 既可以从通道中读取数据,又可以写数据到通道。但流的读写通常是单向的。
- 通道可以异步地读写。
- 通道中的数据总是要先读到一个Buffer,或者总是要从一个Buffer中写入。
Java NIO中最重要的通道的实现:
-
- FileChannel:从文件中读写数据。
- DatagramChannel:能通过UDP读写网络中的数据。
- SocketChannel:能通过TCP读写网络中的数据。
- ServerSocketChannel:可以监听新进来的TCP连接,像Web服务器那样。对每一个新进来的连接都会创建一个SocketChannel。
二 组件buffer
缓冲区本质上是一块可以写入数据,然后可以从中读取数据的内存。这块内存被包装成NIO Buffer对象,并提供了一组方法,用来方便的访问该块内存。
Java NIO 有以下Buffer类型:
-
- ByteBuffer
- MappedByteBuffer
- CharBuffer
- DoubleBuffer
- FloatBuffer
- IntBuffer
- LongBuffer
- ShortBuffer
三 组件selector
Selector(选择器)是Java NIO中能够检测一到多个NIO通道,并能够知晓通道是否为诸如读写事件做好准备的组件。这样,一个单独的线程可以管理多个channel,从而管理多个网络连接。 通常线程越少开销越小,但当前处理器多为多核处理器,多线程任务应该得到充分利用。
一旦向Selector注册了一或多个通道,就可以调用几个重载的select()方法。这些方法返回你所感兴趣的事件(如连接、接受、读或写)已经准备就绪的那些通道。换句话说,如果你对“读就绪”的通道感兴趣,select()方法会返回读事件已经就绪的那些通道。
下面是select()方法:- int select()
- int select(long timeout)
- int selectNow()
select()阻塞到至少有一个通道在你注册的事件上就绪了。
select(long timeout)和select()一样,除了最长会阻塞timeout毫秒(参数)。 selectNow()不会阻塞,不管什么通道就绪都立刻返回(译者注:此方法执行非阻塞的选择操作。如果自从前一次选择操作后,没有通道变成可选择的,则此方法直接返回零。)。 select()方法返回的int值表示有多少通道已经就绪。selector不会主动向通道发送已就绪事件,需要通过select()方法来获取就绪的通道,根据SelectionKey确定是哪个通道,进行相关的业务处理。
示例代码:
1 Selector selector = Selector.open(); 2 channel.configureBlocking(false); 3 SelectionKey key = channel.register(selector, SelectionKey.OP_READ); 4 while(true) { 5 int readyChannels = selector.select(); 6 if(readyChannels == 0) continue; 7 Set selectedKeys = selector.selectedKeys(); 8 Iterator keyIterator = selectedKeys.iterator(); 9 while(keyIterator.hasNext()) { 10 SelectionKey key = keyIterator.next(); 11 if(key.isAcceptable()) { 12 // a connection was accepted by a ServerSocketChannel. 13 } else if (key.isConnectable()) { 14 // a connection was established with a remote server. 15 } else if (key.isReadable()) { 16 // a channel is ready for reading 17 } else if (key.isWritable()) { 18 // a channel is ready for writing 19 } 20 keyIterator.remove(); 21 } 22 }
这个循环遍历已选择键集中的每个键,并检测各个键所对应的通道的就绪事件。
注意每次迭代末尾的keyIterator.remove()调用。Selector不会自己从已选择键集中移除SelectionKey实例。必须在处理完通道时自己移除。下次该通道变成就绪时,Selector会再次将其放入已选择键集中。四 管道pipe
Java NIO 管道是2个线程之间的单向数据连接(用于线程间数据传递)。Pipe有一个source通道和一个sink通道。数据会被写到sink通道,从source通道读取。
直接上示例代码:
1 package com.lanhuigu.nio.channel; 2 3 import java.io.IOException; 4 import java.nio.ByteBuffer; 5 import java.nio.channels.Pipe; 6 7 public class TestPipe { 8 public static void main(String[] args) throws IOException { 9 // 1. 获取管道10 Pipe pipe = Pipe.open();11 12 // 2. 将缓冲区数据写入到管道13 // 2.1 获取一个通道14 Pipe.SinkChannel sinkChannel = pipe.sink();15 // 2.2 定义缓冲区16 ByteBuffer buffer = ByteBuffer.allocate(48);17 buffer.put("发送数据".getBytes());18 buffer.flip(); // 切换数据模式19 // 2.3 将数据写入到管道20 sinkChannel.write(buffer);21 22 // 3. 从管道读取数据23 Pipe.SourceChannel sourceChannel = pipe.source();24 buffer.flip();25 int len = sourceChannel.read(buffer);26 System.out.println(new String(buffer.array(),0,len));27 28 // 4. 关闭管道29 sinkChannel.close();30 sourceChannel.close();31 }32 }