NIO
java 1.4版本引入,給予緩衝區面 向通道的io操做java
bio |
nio |
面向流 |
面向緩衝區(buffer) |
阻塞io |
非阻塞io |
同步 |
同步 |
無 |
Selector(選擇器) |
緩衝區:是一個特定數據類型的容器,有java.nio 包定義,全部的緩衝區都是Buffer抽象類的子類
子類:ByteBuffer CharBuffer ShortBuffer IntBuffer LongBuffer FloatBuffer DoubleBuffer
Buffer主要用於和NIO通道進行通訊,數據從通道讀入到緩衝區,再從緩衝區讀取到通道
Buffer就像是一個數據能夠保存多個類型相同的數據
基本屬性:
1.容量(capacity):表示緩衝區的最大容量 一旦建立不能修改
2.限制(limit):第一個不可讀的索引,即位於limit後面的數據不可讀
3.位置(position):下一個要讀取或寫入數據的索引
4.flip:將此時的position設爲limit,position置爲0 - 通常是從inputChannel將數據讀入到buffer 而後將buffer flip後 爲了從buffer中讀取數據到 outputChannel
5.標記(mark)和恢復(reset):標記是一個索引,經過Buffer.mark()指定一個特定的位置,使用reset方法能夠恢復到這個位置
- 直接緩衝區:程序直接操做物理映射文件
- 非直接緩衝區:jvm - 操做系統 - 物理內存
Channel:相似於流,可是Channel不能直接訪問數據,只能與緩衝區進行交互通道
主體實現類
1.FileChannel:用於讀取 寫入 映射和操做文件的通道
2.DataGramChannel:經過UDP讀取網絡中的數據通道
3.SocketChannel:經過Tcp讀寫通道的數據
4.ServerSocketChannel:能夠監聽新進入的Tcp鏈接,對每個新鏈接建立一個SocketChannel
提供getChannel()方法的類
1.FileInputStream 2.FileOutputStream 3.RandomAccessFile 4.Socket 5.ServerSocket 6.DataGramSocket
通道時間傳輸
1.transferFrom() 2.transferTo()
- 分散讀取(Scatter):將一個
Channel
中的數據分散儲存到多個Buffer
中
- 彙集寫入(Gather):將多個
Buffer
中的數據寫入同一個Channel
中
Selector通常被稱爲選擇器,也被稱爲多路複用器.用於檢查一個或多個通道是否處於可讀可寫如此能夠實現一個線程管理多個Channel
使用Selector帶來的好處有:使用更少的線程來處理Channel,能夠防止上下文切換帶來的性能小號
能夠被選擇(多路複用)的Channel都繼承自SelectableChannel
SelectableChannel
||
AbstractSelectableChannel
|| || ||
DataGramChannel SocketChannel ServerSocketChannel
因此FileChannel不適應與Selector,即不能切換爲非阻塞模式
Selector使用基本步驟
1.建立Selector: Selector selector = Selector.open();
2.設置爲非阻塞爲:channel.configureBlocking(false);
3.註冊Channel到Selector:
/**
* 參數-1:要註冊到的多路複用器
* 參數-2:是一個"interest集合",即要監聽事件的集合(有如下四種)
* OP_CONNECT 鏈接
* OP_ACEEPT 接收
* OP_READ 讀
* OP_WRITE 寫
*/
SelectionKey key = channel.register(selector,SelectionKey.OP_READ);
若是要監聽多種事件以下:
SelectionKey key = channel.register(selector,SelectionKey.OP_CONNECT | SelectionKey.OP_READ);
4.而後就 鏈接就緒 | 接收就緒 | 讀就緒 | 寫就緒
Selector主要方法
方法 |
描述 |
Set<SelectKey> keys() |
返回全部SelectionKey 集合,表明 註冊在這個Selector上 的Channel |
Set<SelectKey> selectedKeys() |
返回已選擇了的(即有io操做的)SelectionKey |
int select() |
監控全部註冊了的Channel ,若是有須要 io的操做時會將對應的selectKey 加入到 selectedKeys 集合中,返回的則是被選擇 (有io操做的)Channel 數量,這個操做時阻 塞的即只有被選擇的Channel 數量>=1才 返回 |
int select(timeout) |
有超時時長,一直沒有io操做的Channel 出現, 到達timeout出現的時間後將自動返回 |
int selectNow() |
無阻塞 當即返回 |
Selector wakeUp() |
使正在select() 當即返回 |
void close() |
關閉 |
SelectionKey主要方法
SelectionKey
表示Channel
和Selector
之間的關係,Channel
向Selector
註冊就會產生一個SelectionKey
緩存
方法 |
描述 |
int interestOps() |
感興趣事件的集合 boolean isInterested = interestSet & SelectionKey.OP_CONNECT ... |
int readyOps() |
獲取通道準備好就緒的操做 |
SelectableChannel channel() |
獲取註冊通道 |
Selector selector() |
獲取選擇器 |
boolean isConnectable() |
檢測Channel 中是否有鏈接事件就緒 |
boolean isAcceptable() |
檢測Channel 中是否有接收事件就緒 |
boolean isReadaable() |
檢測Channel 中是否有讀事件就緒 |
boolean isWriteable() |
檢測Channel 中是否有寫事件就緒 |
Object attach() |
將一個對象附着到SelectionKey 上, 主要是一些用於標識的信息 |
Object attachment() |
獲取註冊信息 也能夠在Channel 註冊的時候附着信息 SelectionKey key = channel.register(selector, SelectionKey.OP_READ, theObject); |
void cancel() |
請求取消此鍵的通道到其選擇器的註冊 |
package com.yuan.nio.selector;
import java.io.IOException;
import java.net.InetSocketAddress;
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.util.Iterator;
public class NioServer {
public static void main(String[] args) throws IOException {
//建立服務端通道
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
//非阻塞模式
serverSocketChannel.configureBlocking(false);
//綁定端口
serverSocketChannel.bind(new InetSocketAddress(9021));
//建立選擇器
Selector selector = Selector.open();
//註冊 接收
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
//有一個事件時就操做
while (selector.select() > 0) {
//獲取事件集合
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()) {
SelectionKey selectionKey = iterator.next();
//若是是接收就緒
if (selectionKey.isAcceptable()) {
//獲取客戶端鏈接
SocketChannel socketChannel = serverSocketChannel.accept();
//切換成非阻塞
socketChannel.configureBlocking(false);
//註冊在多路複用器上 讀
socketChannel.register(selector, SelectionKey.OP_READ);
//讀事件
} else if (selectionKey.isReadable()) {
//獲取客戶端鏈接
SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
//設置緩存
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
int len = 0;
while (-1 != (len = socketChannel.read(byteBuffer))) {
byteBuffer.flip();
System.out.println(new String(byteBuffer.array(),0,len));
byteBuffer.clear();
}
//請求取消此鍵的通道在其選擇器的註冊,也就是 selector.select();的數量 -1 selectionKey.cancel();
socketChannel.close();
}
}
iterator.remove();
}
}
}