javaNio 學習筆記(一)
學習參考文檔:
http://tutorials.jenkov.com/java-nio/index.html
什麼是javaNio
java Nio是java new IO。是java1.4新增的API。它提供了另一種不同於標準IO的實現方式來實現IO
知其然知其所以然
爲什麼要使用java Nio?
弄懂了爲什麼要使用Java NIO
也就是知道我們爲啥要學NIO了。
首先NIO出現就是提供了替代標準IO的一種IO實現方式。那麼爲什麼要替換標準IO呢?
我們來看下下面的例子:
// server
public class Bio {
public static void main(String[] args) {
try {
ServerSocket server = new ServerSocket(2999);
System.out.println("server 監聽2999端口等待客戶端連接" + Instant.now().atOffset(ZoneOffset.ofHours(8)).toInstant());
Socket socket = server.accept();
System.out.println("server 監聽2999端口等待客戶端接受數據" + Instant.now().atOffset(ZoneOffset.ofHours(8)).toInstant());
// 從套接字中獲取輸入流
InputStream inputStream = socket.getInputStream();
byte[] bytes = new byte[1024];
inputStream.read(bytes);
System.out.println("get message from client: " + new String(bytes) + Instant.now().atOffset(ZoneOffset.ofHours(8)).toInstant());
inputStream.close();
socket.close();
server.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
// client
public class Client {
public static void main(String[] args) {
try {
// 建立連接
System.out.println("客戶端準備連接服務器 2999端口" + Instant.now().atOffset(ZoneOffset.ofHours(8)).toInstant());
Socket socket = new Socket("127.0.0.1", 2999);
System.out.println("客戶端連接服務器 2999端口" + Instant.now().atOffset(ZoneOffset.ofHours(8)).toInstant());
// 向socket寫入數據
OutputStream outputStream = socket.getOutputStream();
System.out.println("客戶端連接服務器開始寫入數據" + Instant.now().atOffset(ZoneOffset.ofHours(8)).toInstant());
socket.getOutputStream().write("hi, be happy".getBytes("UTF-8"));
System.out.println("客戶端連接服務器開始寫入完成" + Instant.now().atOffset(ZoneOffset.ofHours(8)).toInstant());
outputStream.close();
socket.close();
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
}
先啓動server,然後在啓動client(爲了讓結果比較清晰建議打斷點運行),返回結果如下
// server 返回結果:
server 監聽2999端口等待客戶端連接2020-06-11T14:35:58.428Z
server 監聽2999端口等待客戶端接受數據2020-06-11T14:36:16.120Z
get message from client: hi, be happy
// client 返回結果
客戶端準備連接服務器 2999端口2020-06-11T14:36:02.026Z
客戶端連接服務器 2999端口2020-06-11T14:36:35.664Z
客戶端連接服務器開始寫入數據2020-06-11T14:36:40.984Z
客戶端連接服務器開始寫入完成2020-06-11T14:36:42.969Z
運行斷點的時候會發現,當server執行到server.accept()
和inputStream.read()
的時候程序會阻塞,讓程序等待,這樣會影響執行效率。這也就是爲什麼會有NIO來替代IO。
先自己思考解決方案
如果是讓我來解決這個問題,該如何處理呢?
可以開線程,每一個線程來處理一個client的連接。這樣即使阻塞也是隻阻塞當前線程。這樣即使阻塞也只是阻塞當前線程,而不會對其他線程造成影響。當然這麼做就會消耗系統資源,因爲線程是有限的,我們不可能無限的開啓線程,若客戶端一直未連接就需要做超時處理,將線程關閉。
Java NIO是如何來做的?
這個問題可能需要等到學習完java NIO
之後纔可能回答的出來。那麼下面我就開始學習Java NIO。
java NIO簡單導讀
參考上面文檔地址
- 傳統
IO
是面向流的,而java NIO
則是面向channels
和buffers
的。數據都是從channel
中讀取到buffer
或者從channel
中寫入buffer
。 java NIO
是非阻塞IO,即不會產生上面的問題。線程可以讓channel
(有的人叫通道)去讀取buffer
中的數據,這個時候線程可以做其他事情。當數據程從channel
讀取到buffer
的時候線程在繼續處理這部分事情。對於寫也是一樣。Java NIO
還提供了一個selector
。selector
是一個對象,可以監控多個通道的事件(如:連接打開,數據到達等)。這樣可以讓單個線程監視多個channel
java NIO 基本概念
java NIO主要的核心組件如下:
- Channels(通道、管道、頻道)
- buffers(數據緩衝區)
- Selector(選擇器,從導讀的概念來看感覺有點想監聽器)
當然java NIO還有一些其他組件,後面我會慢慢進行學習。
channel
和buffer
就可以理解潮汐車道倆頭的地點。數據可以從頻道(channel)中寫入緩衝區(buffer).
也可以從緩衝區讀取到頻道中。
主要實現的管道類型
- FileChannel
- DatagramChannel
- SocketChannel
- ServerSocketChannel
核心的緩衝區
- ByteBuffer
- CharBuffer
- DoubleBuffer
- FloatBuffer
- IntBuffer
- LongBuffer
- ShortBuffer
選擇器
選擇器的作用就是能讓單個線程來處理多線程的事情。要使用選擇器,需要註冊頻道。然後調用它的select()方法。此方法將阻塞,直到爲其中一個已註冊通道準備好事件爲止。方法返回後,線程就可以處理這些事件。(有傳入的連接,接收到的數據等等)
這些類型後續學習中我會進行聯繫並儘量做詳細瞭解