【從入門到放棄-Java】併發程式設計-NIO使用
阿新 • • 發佈:2019-08-20
前言
上文【從入門到放棄-SpringBoot】SpringBoot原始碼分析-請求過程中我們瞭解到,tomcat接收、返回請求的過程都是基於NIO實現的。日常工作中有很多基於NIO的使用,我們知道NIO可以提高系統的併發度,接下來的系列我們來深入學習下NIO,本文先從使用上簡單概述。
NIO概述
NIO即non-blocking(New IO),是指jdk1.4 及以上版本里提供的新api。
NIO和IO最大的區別:IO是以流的方式處理資料,而NIO是以塊的方式處理資料;IO對事件的處理是阻塞的,NIO是非阻塞的
NIO的核心部分:
- Channel
- Buffer
- Selector
NIO主要分為標準輸入輸出和網路請求
標準輸入輸出NIO
讀取
private static void readNio() { try { //1、開啟檔案讀取流 FileInputStream fileInputStream = new FileInputStream("/Users/my/Desktop/123.txt"); //2、獲取fileChannel FileChannel channel = fileInputStream.getChannel(); //3、設定ByteBuffer大小,一次能容納capacity位元組 int capacity = 9; ByteBuffer bf = ByteBuffer.allocate(capacity); //4、當read返回-1時,表示檔案讀取完畢 int length = -1; while ((length = channel.read(bf)) != -1) { byte[] bytes = bf.array(); System.out.println(new String(bytes, 0, length)); //4、將bf position置為0,方便下次讀取 bf.clear(); } channel.close(); fileInputStream.close(); } catch (IOException e) { e.printStackTrace(); } }
寫入
private static void writeNio() { try { //1、開啟檔案寫入流 FileOutputStream fileOutputStream = new FileOutputStream("/Users/my/Desktop/123.txt"); //2、獲取fileChannel FileChannel channel = fileOutputStream.getChannel(); //3、初始化byteBuffer String str = "薩達案發生大大sdada34;sdds'"; ByteBuffer bf = ByteBuffer.allocate(1024); //4、將bf position置為0,方便下次讀取 bf.clear(); //5、從byteBuffer的position位置填充byte bf.put(str.getBytes()); //6、將bf position置為0,limit設定為position避免寫入內容過多 bf.flip(); int length = 0; //7、如果position小於limit即未寫入完畢 while (bf.hasRemaining()) { //8、將buffer內容寫入channel length = channel.write(bf); System.out.println(bf); } channel.close(); fileOutputStream.close(); } catch (IOException e) { e.printStackTrace(); } }
網路NIO
服務端
package com.my.tools.nio;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
public class ServerSocket {
private static ServerSocket serverSocket;
private Selector selector;
public static void main(String[] args) throws Exception {
ServerSocket.getInstance().init(8001).listen();
}
public static ServerSocket getInstance() {
if (serverSocket == null) {
synchronized (ServerSocket.class) {
if (serverSocket == null) {
serverSocket = new ServerSocket();
}
}
}
return serverSocket;
}
public ServerSocket init(int port) throws IOException {
//初始化channel
ServerSocketChannel server = ServerSocketChannel.open();
//繫結本機8001埠
server.socket().bind(new InetSocketAddress(8001));
//設定為非阻塞模式
server.configureBlocking(false);
//開啟selector管理器
selector = Selector.open();
//將selector註冊至server,並設定只處理accept事件
server.register(selector, SelectionKey.OP_ACCEPT);
return this;
}
public void listen() throws Exception {
System.out.println("server start");
//無限迴圈持續監聽
while (true) {
//會阻塞 直到監聽到註冊的事件
selector.select();
//獲取喚醒的事件
Iterator<SelectionKey> selectorKeys = selector.selectedKeys().iterator();
while (selectorKeys.hasNext()) {
SelectionKey key = selectorKeys.next();
//將已取出的SelectionKey刪除,防止重複處理
selectorKeys.remove();
if (key.isAcceptable()) {
//獲取到服務端的socket
ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();
//獲取接收到的客戶端socket
SocketChannel socketChannel = serverSocketChannel.accept();
socketChannel.configureBlocking(false);
//向客戶端寫訊息
socketChannel.write(ByteBuffer.wrap(new String("hello, this is server").getBytes()));
//註冊監聽read事件
socketChannel.register(selector, SelectionKey.OP_READ);
System.out.println("accept");
} else if (key.isReadable()) {
//使用selector獲取channel
SocketChannel socketChannel = (SocketChannel) key.channel();
socketChannel.configureBlocking(false);
ByteBuffer buffer = ByteBuffer.allocate(1024);
//讀訊息
int length = socketChannel.read(buffer);
String string = new String(buffer.array(), 0 , length);
System.out.println("read:" + socketChannel + string);
//寫訊息
socketChannel.write(ByteBuffer.wrap(("server " + System.currentTimeMillis()).getBytes()));
Thread.sleep(10000);
}
}
}
}
}
客戶端
package com.my.tools.nio;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
public class ClientSocket {
public static ClientSocket clientSocket;
private static Selector selector;
public static void main(String[] args) throws Exception {
ClientSocket.getInstance().init("localhost", 8001).listen();
}
public static ClientSocket getInstance() {
if (clientSocket == null) {
synchronized (ClientSocket.class) {
if (clientSocket == null) {
clientSocket = new ClientSocket();
}
}
}
return clientSocket;
}
public ClientSocket init(String ip, int port) throws IOException {
SocketChannel socketChannel = SocketChannel.open();
socketChannel.connect(new InetSocketAddress(ip, port));
socketChannel.configureBlocking(false);
selector = Selector.open();
socketChannel.register(selector, SelectionKey.OP_CONNECT | SelectionKey.OP_READ);
return this;
}
public void listen() throws Exception {
System.out.println("client start");
while (true) {
selector.select();
Iterator<SelectionKey> selectionKeys = selector.selectedKeys().iterator();
while (selectionKeys.hasNext()) {
SelectionKey selectionKey = selectionKeys.next();
selectionKeys.remove();
if (selectionKey.isConnectable()) {
SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
socketChannel.configureBlocking(false);
ByteBuffer buffer = ByteBuffer.wrap(new String("hello, this is client").getBytes());
socketChannel.write(buffer);
socketChannel.register(selector, SelectionKey.OP_READ);
System.out.println("client write");
} else if (selectionKey.isReadable()) {
SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
socketChannel.configureBlocking(false);
ByteBuffer buffer = ByteBuffer.allocate(1024);
int length = socketChannel.read(buffer);
System.out.println("client read: " + socketChannel + new String(buffer.array(), 0, length));
socketChannel.write(ByteBuffer.wrap(("client " + System.currentTimeMillis()).getBytes()));
Thread.sleep(10000);
}
}
}
}
}
總結
上述示例展示了最簡單的檔案NIO和網路NIO用法,接下來會深入分析每個方法的原始碼,並對效能進行調優。
更多文章見:https://nc2era.com
作者:aloof_
原文連結
本文為雲棲社群原創內容,未經