文章目錄
- 一、IO讀寫原理
- 二、四種主要的IO模型
-
- 概念
- 1.同步阻塞 BIO
-
- BIO程式設計
- 2.同步非阻塞 NIO
- 3.IO多路複用
- 4.異步非阻塞 AIO
一、IO讀寫原理
-
檔案的讀寫還是socket讀寫,再Java應用層開發,都是 input或者 output處理。使用者程式進行的IO讀寫,會用到 read&write(核心态) 兩大系統調用。
read:将資料從核心緩沖區複制到程序緩沖區
write:把資料從程序緩沖區複制到核心緩沖區
-
核心緩沖區 和 程序緩存區
使用者程序(N個):處于使用者态(使用者空間)
系統空間:核心态。在使用者态需要通路系統資源,需要借助核心态,系統資源主要有:
1)CPU:控制一個程式的執行
2)輸入輸出:一切都是流,所有流都需要借助核心态
3)程序管理:程序建立、銷毀、阻塞、喚醒之間的排程
4)記憶體:記憶體的申請、釋放
5)程序間通信:程序之間不能互相通路記憶體,是以程序
以上所提到的系統資源,再使用者程序中是無法被直接通路的,隻有通過作業系統來通路,是以把作業系統通路這一功能稱之為系統調用。
-
緩沖區的目的,是為了減少頻繁的系統IO調用
系統調用需要從使用者态切換到核心态,切換之後儲存使用者程序的資料和狀态等資訊。結束調用之後會需要恢複之前的資訊,為了減少這種損耗的時間,還有損耗性能的時間,是以出現了緩沖區。
- 有了緩沖區,作業系統使用 read從核心緩沖區複制到程序緩沖區,write從程序緩沖區複制到核心緩沖區,隻有緩沖區中的資料達到一定的量,再進行IO的系統調用,提升了系統的性能。
- Java IO讀寫底層的流程圖:
二、四種主要的IO模型
概念
-
阻塞IO:需要核心IO操作徹底完成之後,才傳回到使用者空間,執行使用者的操作
非阻塞IO:不需要等待核心IO操作徹底完成之後,才傳回到使用者空間
阻塞/非阻塞 指的是使用者空間程式的執行狀态
-
同步IO:是使用者空間線程和核心空間線程的互動,使用者空間線程是主動發起IO請求的一方,核心空間線程指的是被動接收的一方
異步IO:與上面相反
1.同步阻塞 BIO
-
jdk1.4之前,IO模型都采用的BIO模型。
需要先在伺服器端啟動一個ServerSocket,然後在用戶端啟動Socket與伺服器端進行通信,伺服器端調用accept方法來接收用戶端的連接配接請求,一旦接收上連接配接請求,就可以建立套接字,在這個套接字上進行讀寫操作,此時不能再接收其他用戶端的連接配接請求,隻能等待目前連接配接的用戶端的執行操作完成。
-
JDK1.4開始,出現同步阻塞式IO
處理多個用戶端請求,使用多線程
優點:使用者線程阻塞等待資料期間,不會占用CPU資源
缺點:BIO模型在高并發的場景下是不可用的
BIO程式設計
-
伺服器端
1)建立ServerSocket執行個體
2)綁定端口
3)通過accept方法監聽用戶端的連接配接,有用戶端連接配接會傳回socket執行個體
4)進行讀寫操作
5)關閉資源
-
伺服器端如何設計為可處理很多用戶端的連接配接?
主線程負責接收用戶端連接配接,子線程負責和用戶端互動。
代碼實作:
//子線程
class HandlerThread extends Thread{
private Socket socket;
public HandlerThread(Socket socket){
this.socket = socket;
}
@Override
public void run() {//重寫run方法
try {
//讀取用戶端的請求資訊
OutputStream output = null;
BufferedReader reader = null;
while(true){
reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String msg = reader.readLine();
System.out.println("用戶端:"+socket.getRemoteSocketAddress()+",發送的資訊為:"+msg);
//給用戶端回複
output = socket.getOutputStream();//擷取輸出流
//控制台讀取資料
reader = new BufferedReader(new InputStreamReader(System.in));
String info = reader.readLine();
output.write((info+"\n").getBytes());
output.flush();
if("".equals(info) || "exit".equals(info)) break;
}
//關閉資源
reader.close();
output.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
System.out.println("用戶端:"+socket.getRemoteSocketAddress()+"關閉了");
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
//主線程
public class MyBIOServer {
public static void main(String[] args) {
ServerSocket ssocket = null;
try {
//建立ServerSocket執行個體
ssocket = new ServerSocket();
//綁定端口
ssocket.bind(new InetSocketAddress(9999));
System.out.println("伺服器端啟動了...");
while(true){
//通過accept方法監聽用戶端的連接配接,有用戶端連接配接會傳回socket執行個體
Socket socket = ssocket.accept(); //這個方法是一個阻塞方法
System.out.println("有新的用戶端連接配接:"+socket.getRemoteSocketAddress());
//将socket執行個體交給子線程去處理
new HandlerThread(socket).start();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if(ssocket != null){
try {
ssocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
-
用戶端
1)建立socket執行個體
2)通過connect去指定伺服器端的IP和端口
3)進行讀寫操作
4)關閉資源
代碼實作:
public class MyBIOClient {
public static void main(String[] args) {
Socket socket = null;
try {
//建立Socket執行個體
socket = new Socket();
//連接配接伺服器端,通過connect綁定IP位址和端口号
socket.connect(new InetSocketAddress("127.0.0.1", 9999));
System.out.println("用戶端啟動了...");
//進行讀寫操作
while(true){
//寫操作
OutputStream output = socket.getOutputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
output.write((reader.readLine()+"\n").getBytes());
output.flush();
//讀操作
reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String msg = reader.readLine();
System.out.println("伺服器響應資訊為:"+msg);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if(socket != null){
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
2.同步非阻塞 NIO
NIO的内容在NIO模型中具體描述。
3.IO多路複用
- 通過一個新的系統調用 select/epoll 系統調用,實作一個程序監視多個檔案描述符(唯一ID)。一旦某個描述符就緒(核心緩沖區可讀/可寫),核心能夠通知使用者程式進行IO系統調用。
- 以線程為例:單個線程不斷的輪詢 select/epoll 系統調用 所負責多個socket連接配接,當某個socket連接配接有資料到達,就傳回這些可以讀寫的連接配接。
-
特點:
與NIO模型類似,多路複用IO需要輪詢負責select/epoll查詢調用的線程,查找出可以進行IO操作的一個連接配接,對于每一個可以查詢的socket,一般需要設定為non-blocking。
-
優點:
select/epoll 可以同時處理成百上千的連接配接,與之前的一個線程維護一個連接配接相比,IO多路複用則不需要建立線程,也就不需要維護,進而減少系統開銷。
-
缺點:
select/epoll系統調用,屬于阻塞的模式,同步IO。讀寫事件就緒之後,使用者自己進行讀寫,這個讀寫過程也是阻塞的。
4.異步非阻塞 AIO
- 使用者線程發起系統調用,告知核心啟動某個IO操作,使用者線程直接傳回。核心在整個IO操作(資料準備,資料複制完成之後,調用回調函數通知使用者程式(已就緒),使用者執行後續的業務操作。
-
特點:
分為兩個等待過程,等待資料就緒,等待資料拷貝,而在這兩個等待過程中使用者線程都不是block的,等這兩個操作完成之後,使用者線程就會收到一個信号。是以AIO又稱之為信号驅動IO。
-
缺點:
需要事件的注冊,就需要作業系統,對系統資源和記憶體資源消耗大。
-
java.nio.channels包:
AsynchronousServerSocketChannel 伺服器資料通道
AsynchronousSocketChannel 用戶端通道
AsynchronousDatagramChannel 基于UDP的通道
AsynchronousFIleChannel 操作檔案
- 異步非阻塞,伺服器端實作一個一個線程,異步執行目前用戶端請求,用戶端的請求都是作業系統完成再去通知伺服器端處理,是以這個AIO主要用于連接配接數多同時請求比較複雜的系統架構。
BIO NIO AIO三個網絡模型的适用場景:
模型 | 适用場景 |
---|---|
BIO | 連接配接數目少的系統架構,同時也需要伺服器資源充足,雖然效率不高,但是程式… |
NIO | 連接配接數目比較多以及連接配接時間短的系統架構,例如聊天伺服器,程式設計比較複雜 |
AIO | 連接配接數目比較多以及連接配接時間長的系統架構,充分調用作業系統參與并發,不管是業務邏輯,還是并發邏輯,都需要作業系統的支援,是以不同的作業系統,性能也是不同的 |