NIO三大元件的關系:
三大元件:selector,channel,buffer
- 一個Select對應一個線程,一個線程對應多個channel
- 程式切換到那個channel是由事件決定的
- Selector會根據不同的事件,在各個通道上切換
- Buffer是一個記憶體塊,底層是一個資料
- 資料的讀取寫入是通過Buffer.在傳統Bio中要麼是輸入流,要麼是輸出流,不能是雙向,但是NIO的Buffer是可以讀也可以寫,需要通過flip方法進行切換
- channel是雙向的
- 在傳統的BIO模型中,如果一個請求沒有攜帶資料或者傳回資料,可能會造成線程阻塞,但是在NIO模型中,資料被緩沖在Buffer裡,用戶端隻能從Buffer中讀取資料及發送資料,如果緩沖區中沒有資料,伺服器的Select就不會區處理這個通道,而是處理其他Buffer區中有資料的通道
Nio與Bio的比較
元件一 Buffer
簡單demo
public static void main(String[] args) {
//建立一個Buffer,可以存放5個Int類型資料
IntBuffer intBuffer=IntBuffer.allocate(5);
for(int i=0;i<intBuffer.capacity();i++){
//放資料
intBuffer.put(i*2);
}
//将buffer轉換,讀寫切換(!!!!!)
intBuffer.flip();
while(intBuffer.hasRemaining()){
System.out.println(intBuffer.get());
}
}
常用Buffer子類
常用方法
buffer類的常用方法總結
元件二 通道channel
基本介紹
n類似于流,能夠同時進行讀寫操作
可以實作異步讀取資料
可以從緩沖區buffer讀取資料
案例一:本地檔案寫資料
其實FileChannel是對輸出流的一個包裝
代碼實列:’
public static void main(String[] args) throws IOException {
String str="hello world";
//建立一個輸出流
FileOutputStream stream=new FileOutputStream("D://Aimg//test/5.txt");
//這個fileChanel真是類型是FileChannelImpl
FileChannel channel=stream.getChannel();
//建立一個緩沖區
ByteBuffer byteBuffer=ByteBuffer.allocate(1024);
//放str入byteBuffer
byteBuffer.put(str.getBytes());
//讀寫反裝
byteBuffer.flip();
//将緩沖區資料寫入通道
channel.write(byteBuffer);
stream.close();
}
案例二:本地檔案讀資料
public static void main(String[] args) throws IOException {
//檔案
File file=new File("D://Aimg//test/5.txt");
FileInputStream inputStream=new FileInputStream(file);
FileChannel channel=inputStream.getChannel();
ByteBuffer byteBuffer=ByteBuffer.allocate((int)file.length());
//将通道資料讀入到buffer中,注意,write方法是從緩沖區讀取資料到通道
channel.read(byteBuffer);
System.out.println(new String(byteBuffer.array()));
}
上述兩個案列的示意圖:
案例三:一個Buffer完成檔案的讀寫
流程:
public static void main(String[] args) throws IOException {
//檔案
File file=new File("D://Aimg//test/5.txt");
FileInputStream inputStream=new FileInputStream(file);
FileChannel readChannel=inputStream.getChannel();
FileOutputStream outputStream=new FileOutputStream("D://Aimg//test/6.txt");
FileChannel writeChannel=outputStream.getChannel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
while(true){
//每次讀的時候要記得清空緩存
buffer.clear();
int read= readChannel.read(buffer);
if(read!=-1){
//讀寫反裝
buffer.flip();
writeChannel.write(buffer);
}else break;
}
}
案例四:channel的transferFrom拷貝檔案
public static void main(String[] args) throws IOException {
//檔案
File file=new File("D://Aimg//12.jpg");
FileInputStream inputStream=new FileInputStream(file);
FileChannel readChannel=inputStream.getChannel();
FileOutputStream outputStream=new FileOutputStream("D://Aimg//13.jpg");
FileChannel writeChannel=outputStream.getChannel();
writeChannel.transferFrom(readChannel,0,readChannel.size());
}
元件三 Selector選擇器
常用方法
一個channel通道對應了一個selectKey,每次有事件發生時,selector會根據擷取的selectKeys數組來周遊channel
Selector關系圖及解析:
說明:
當用戶端連接配接時,會通過ServerSocketChannel
,得到SocketChannel
将socketChannel注冊到Selector上,并且傳回一個
selectorKey,該SelectorKey會和Selector關聯
- selector進行監聽select方法,傳回有事件發生的通道個數
- 進一步得到各個SelectKey
- 再通過SelectKey,反向擷取channel
- 最後通過channel完成對應的事件