NIO(New IO)是从Java 1.4版开始引入的新的IO API,其与标准JAVA IO API的差异本质上体现在资源的利用方式上,这一点可以从现实中餐厅排队的例子来理解。午饭时间到了,小明准备从三家备选餐厅A、B、C中选择一家就餐,糟糕的是三家餐厅的位置都满了,小明只能选择一家餐厅开始排队等待,还有一个办法就是小明让自己的朋友小华和小丽去另外两家排队,这样才能尽快就餐,这就好比传统的的IO,没有输入时线程就一直阻塞,且每个输入通道都需要一个线程来读取。而现在流行的就餐方式是小明在A、B、C各取一个号且关注餐厅排队就餐的微信公众号,这时小明就可以去干别的事情,直到微信提醒某家餐厅有位置了,好处显而易见,既解放了生产力,又节约了时间,资源利用更高效,这就是NIO处理输入输出的方式。
总的来说,NIO有三大特点,第一个特点就是同时拥有阻塞模式和非阻塞模式,可以实现异步IO,下面分别给出IO服务器和NIO服务器的一个例子:
public class IOServer {
public static void main(String[] args) {
try {
ServerSocket ss = new ServerSocket(8189);
while (true) {
Socket socket = ss.accept();
Runnable runnable = new SocketHandlerThread(socket);
Thread t = new Thread(runnable);
t.start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
class SocketHandlerThread implements Runnable {
private Socket socket;
public SocketHandlerThread(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
InputStream inputStream = socket.getInputStream();
OutputStream outputStream = socket.getOutputStream();
Scanner scanner = new Scanner(inputStream);
PrintWriter out = new PrintWriter(outputStream, true);
out.println("Enter something(enter exit to quit):");
boolean done = false;
while (!done && scanner.hasNextLine()) {
String line = scanner.nextLine();
out.println("Your input: " + line);
if (line.trim().equalsIgnoreCase("exit")) {
done = true;
}
}
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
本地启动IO服务器后,windows下调出dos控制台,输入telnet localhost 8189,连接上服务器,可以输入任意字符,输入”exit“跟服务器断开连接:
可以使用多个窗口开启多个socket连接,从代码中可以看出,每有一个socket连接就开启一个处理线程,因为主线程会在accept()函数处阻塞直到有新的连接进来,所有必须新开处理线程来处理socket连接的输入输出,在socket处理线程中,程序会阻塞在scanner.hasNextLine()这里,直至新的输入进来,这就是传统IO的阻塞特性。
public class NIOServer {
public static void main(String[] args) {
try {
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.bind(new InetSocketAddress(8189));
ssc.configureBlocking(false); // 设定为非阻塞
List
scList = new ArrayList<>();
ByteBuffer buffer = ByteBuffer.allocate(48);
while (true) {
SocketChannel sc = ssc.accept(); // 此处会阻塞
if (sc != null) {
scList.add(sc);
System.out.println("new socket");
sc.configureBlocking(false); // 设定为非阻塞
}
for (SocketChannel scTemp : scList) {
scTemp.read(buffer); // 此处会阻塞
}
buffer.flip();
while (buffer.hasRemaining()) {
System.out.print((char) buffer.get());
}
buffer.clear();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}