NIO編程介紹
I/O模型
java支持3種網絡編程模型I/O模式:BIO(同步並阻塞)、NIO(同步非阻塞)、AIO(異步非阻塞)
阻塞指的是訪問IO的線程是否會阻塞(或等待)。線程訪問資源,該資源是否準備就緒的一種處理方式。
阻塞與非阻塞:

同步與異步:

1.1 BIO
BIO:同步阻塞,服務器實現模式為一個連接一個線程,即客戶端有連接請求時服務器端就需要啟動一個線程進行處理,如果這個連接不做任何事情會造成不必要的線程開銷,可以通過線程池機制改善。

代碼示例:
public class ServerDemo {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket=new ServerSocket(9999);
ExecutorService executorService = Executors.newCachedThreadPool();
while (true){
Socket socket=serverSocket.accept();
System.out.println("有客戶端連接");
executorService.execute(new Runnable() {
@Override
public void run() {
InputStream inputStream = null;
try {
inputStream = socket.getInputStream();
byte[] b=new byte[1024];
int read = inputStream.read(b);
System.out.println("收到客戶端信息:"+new String(b,0,read));
OutputStream outputStream = socket.getOutputStream();
outputStream.write("你好客戶端".getBytes(Charset.defaultCharset()));
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
}
}
public class ClientDemo {
public static void main(String[] args) throws IOException {
Socket socket=new Socket("127.0.0.1",9999);
OutputStream outputStream = socket.getOutputStream();
outputStream.write("你好服務端".getBytes(Charset.defaultCharset()));
InputStream inputStream = socket.getInputStream();
byte[] bytes=new byte[1024];
inputStream.read(bytes);
System.out.println("收到服務端消息:"+new String(bytes));
socket.close();
}
}
BIO問題分析:
- 每個請求都需要創建獨立的線程,與對應的客戶端進行數據Read、業務處理、數據Write
- 並發數較大時,需要創建大量線程來處理連接,系統資源佔用較大
- 連接建立後,如果當前線程暫時沒有數據可讀,則線程就阻塞在Read操作上,造成資源阻塞。
1.2 NIO
NIO:同步非阻塞,服務器實現模式為一個線程處理多個請求(連接),即客戶端發送的連接請求都會註冊到多路復用器上,多路復用器輪詢到連接有I/O請求就進行處理。

代碼示例:
public class NioSelectorServer {
public static void main(String[] args) throws IOException, InterruptedException {
//1.打開服務器通道
ServerSocketChannel serverSocketChannel=ServerSocketChannel.open();
//2.綁定端口號
serverSocketChannel.bind(new InetSocketAddress(9999));
//3.設置通道為非阻塞
serverSocketChannel.configureBlocking(false);
//4.創建選擇器
Selector selector = Selector.open();
//5.將服務端通道註冊到選擇器上,並指定註冊監聽事件為OP_ACCEPT
serverSocketChannel.register(selector,SelectionKey.OP_ACCEPT);
while (true){
//6.檢查選擇器釋放有事件
// int select = selector.select(2000);
int select = selector.select();
if(select==0){
System.out.println("無連接");
continue;
}
//7.獲取事件集合
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeys.iterator();
while (iterator.hasNext()){
SelectionKey selectionKey = iterator.next();
//8.判斷事件是否是連接事件
if(selectionKey.isAcceptable()){
//9.得到客戶端通道,並將通道註冊到選擇器上
SocketChannel socketChannel = serverSocketChannel.accept();
System.out.println("有客戶端連接");
socketChannel.configureBlocking(false);
socketChannel.register(selector,SelectionKey.OP_READ);
}
//10.判斷是否是讀就緒事件
else if(selectionKey.isReadable()){
SocketChannel channel = (SocketChannel)selectionKey.channel();
ByteBuffer byteBuffer=ByteBuffer.allocate(1024);
//11.得到客戶端通道,讀取數據到緩衝區
int read = 0;
read = channel.read(byteBuffer);
System.out.println("客戶端消息:"+new String(byteBuffer.array()));
System.out.println("模擬業務處理中。。。");
Thread.sleep(5000);
if(read>0){
//12.回寫數據給客戶端
channel.write(ByteBuffer.wrap("你好客戶端".getBytes(StandardCharsets.UTF_8)));
channel.close();
}
}
//13.從集合刪除對應事件,防止二次處理
iterator.remove();
}
}
}
}
1.3 AIO
AIO引入了異步通道的概念,採用了Proactor模式,簡化了程序編寫。它的特點是先由操作系統完成後才通知服務端程序啟動線程去處理,一般適用於連接數較多且連接時間比較長的應用。
Proactor模式是一個消息異步通知的設計模式,Proactor通知的部署就緒事件,而是操作完成事件。
適用場景:
- BIO適用於連接數比較小且固定的架構,這種方式對服務器資源要求比較高
- NIO適用於連接數目多且比較短的架構,比如聊天服務器,彈幕系統,服務期間通訊等
- AIO適用於連接數多且連接比較長的架構,比如相冊服務器。
NIO編程
2.1 NIO介紹
Java NIO是JDK提供的新API。NIO有三個核心部分:Channel,Buffer,Selector。NIO是面向緩衝區編程的。JAVA NIO的非阻塞模式,是一個線程從某個通道發送或讀取數據,僅能得到目前可用的數據,如果目前沒有數據可用時,就什麼都不會獲取,而不是阻塞線程。
通俗理解:NIO是可以做到用一個線程來處理多個操作的。假設有1000個請求過來,根據實際情況,可以分配50或100個線程來處理。不像BIO那樣,非得分配1000個線程。
NIO和BIO比較
- BIO是以流的方式處理數據,而NIO是以緩衝區的方式處理數據
- BIO是阻塞的,NIO是非阻塞的
- BIO基於位元組流和字符流操作,而NIO是基於Channel和Buffer進行操作。NIO基於Selector監聽多個通道的事件。


