Java共支持3種網絡編程模型/IO模式:BIO、NIO、AIO
1)Java BIO : 同步並阻塞(傳統阻塞型),服務器實現模式爲一個連接一個線程,即客戶端有連接請求時服務器端就需要啓動一個線程進行處理,如果這個連接不做任何事情會造 成不必要的線程開銷
2)Java NIO : 同步非阻塞,服務器實現模式爲一個線程處理多個請求(連接),即客戶端發送的連接請求都會註冊到多路複用器上,多路複用器輪詢到連接有I/O請求就進行處理
3)Java AIO(NIO.2) : 異步非阻塞,AIO 引入異步通道的概念,採用了 Proactor 模式,簡化了程序編寫,有效的請求才啓動線程,它的特點是先由操作系統完成後才通知服務端程序啓動線程去處理,一般適用於連接數較多且連接時間較長的應用
BIO、NIO、AIO適用場景分析
BIO方式適用於連接數目比較小且固定的架構,這種方式對服務器資源要求比較高,併發侷限於應用中,JDK1.4以前的唯一選擇,但程序簡單易理解。
NIO方式適用於連接數目多且連接比較短(輕操作)的架構,比如聊天服務器,彈幕系統,服務器間通訊等。編程比較複雜,JDK1.4開始支持。
AIO方式使用於連接數目多且連接比較長(重操作)的架構,比如相冊服務器,充分調用OS參與併發操作,編程比較複雜,JDK1.7開始支持。
爲什麼說BIO是阻塞的呢?下面進行分析,先看一段BIO的Demo代碼:
package com.bio.test;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class BIOServer {
public static void main(String[] args) throws Exception {
//線程池機制
//思路
//1. 創建一個線程池
//2. 如果有客戶端連接,就創建一個線程,與之通訊(單獨寫一個方法)
ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
//創建ServerSocket
ServerSocket serverSocket = new ServerSocket(6666);
System.out.println("服務器啓動了");
while (true) {
System.out.println("線程信息 id =" + Thread.currentThread().getId() + " 名字=" + Thread.currentThread().getName());
//監聽,等待客戶端連接
System.out.println("等待連接....");
//這裏產生了阻塞
final Socket socket = serverSocket.accept();
System.out.println("連接到一個客戶端");
//就創建一個線程,與之通訊(單獨寫一個方法)
newCachedThreadPool.execute(new Runnable() {
public void run() { //我們重寫
//可以和客戶端通訊
handler(socket);
}
});
}
}
//編寫一個handler方法,和客戶端通訊
public static void handler(Socket socket) {
try {
System.out.println("線程信息 id =" + Thread.currentThread().getId() + " 名字=" + Thread.currentThread().getName());
byte[] bytes = new byte[1024];
//通過socket 獲取輸入流
InputStream inputStream = socket.getInputStream();
//循環的讀取客戶端發送的數據
while (true) {
System.out.println("線程信息 id =" + Thread.currentThread().getId() + " 名字=" + Thread.currentThread().getName());
System.out.println("read....");
//這裏產生了阻塞
int read = inputStream.read(bytes);
if(read != -1) {
System.out.println(new String(bytes, 0, read
)); //輸出客戶端發送的數據
} else {
break;
}
}
}catch (Exception e) {
e.printStackTrace();
}finally {
System.out.println("關閉和client的連接");
try {
socket.close();
}catch (Exception e) {
e.printStackTrace();
}
}
}
}
在30行處代碼和57行處代碼都產生了阻塞。
30行處代碼是serverSocket等待客戶端的連接,每當產生一個連接,就會分配一個socket進行處理。
57行處代碼是程序獲取了socket鏈接後,從socket那裏把inputStream管道拿出來,然後從inputStream管道那裏等待用戶數據時產生了阻塞;由此可以看出 BIO 是阻塞的。
簡單來講,serverSocket負責分配 socket 連接,而 socket 具體負責進行讀寫操作。無論 serverSocket 還是 socket,在工作過程中都會產生阻塞。