目录
- Java BIO
- Java NIO
- Java AIO
- 小结
在Java中,一共有三种IO模型,分别是阻塞IO(BIO)
、非阻塞IO(NIO)
和异步IO(AIO)
。
- BioClient
/** * @Author 三分恶 * @Date 2023/4/30 * @Description BIO客户端 */ public class BioClient { public static void main(String[] args) throws IOException { List<String> names= Arrays.asList("帅哥","靓仔","坤坤"); //通过循环创建多个多个client for (String name:names){ //创建socket并根据IP地址与端口连接服务端 Socket socket=new Socket("127.0.0.1",8888); System.out.println("===========BIO客户端启动================"); //从socket中获取字节输出流 OutputStream outputStream=socket.getOutputStream(); //通过输出流向服务端传递信息 String hello="你好,"+name+"!"; outputStream.write(hello.getBytes()); //清空流,关闭socket输出 outputStream.flush(); socket.shutdownOutput(); //从socket中获取字节输入流 InputStream inputStream=socket.getInputStream(); BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(inputStream)); //读取服务端消息 String msg; while((msg=bufferedReader.readLine())!=null){ System.out.println("收到服务端消息:"+msg); } inputStream.close(); outputStream.close(); socket.close(); } } }
- 先启动
BioServer
,再启动BioClient
,运行结果
===========BIO服务端启动================
收到客户端消息:你好,帅哥!
收到客户端消息:你好,靓仔!
收到客户端消息:你好,坤坤!
===========BIO客户端启动================
收到服务端消息:你好,吊毛!
===========BIO客户端启动================
收到服务端消息:你好,吊毛!
===========BIO客户端启动================
收到服务端消息:你好,吊毛!
在上述Java-BIO
的通信过程中,如果客户端一直没有发送消息过来,服务端则会一直等待下去,从而服务端陷入阻塞状态。同理,由于客户端也一直在等待服务端的消息,如果服务端一直未响应消息回来,客户端也会陷入阻塞状态。
在BioServer
定义了一个类BioServerThread
,继承了Thread
类,run
方法里主要是通过socket和流来读取客户端的消息,以及发送消息给客户端,每处理一个客户端的Socket连接,就得新建一个线程。
同时,IO读写操作也是阻塞的,如果客户端一直没有发送消息过来,线程就会进入阻塞状态,一直等待下去。
在BioClient
里,循环创建Socket
,向服务端收发消息,客户端的读写也是阻塞的。
在这个Demo里就体现了BIO的两个特点:
- 一个客户端连接对应一个处理线程
- 读写操作都是阻塞的
- NioClient
public class NioClient { public static void main(String[] args) throws IOException { // 创建SocketChannel并指定ip地址和端口号 SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 8888)); System.out.println("==============NIO客户端启动================"); // 非阻塞模式 socketChannel.configureBlocking(false); String hello="你好,靓仔!"; ByteBuffer buffer = ByteBuffer.wrap(hello.getBytes()); // 向通道中写入数据 socketChannel.write(buffer); System.out.println("发送消息:" + hello); buffer.clear(); // 将channel注册到Selector并监听READ事件 socketChannel.register(Selector.open(), SelectionKey.OP_READ, buffer); while (true) { // 读取服务端数据 if (socketChannel.read(buffer) > 0) { buffer.flip(); String msg = new String(buffer.array(), 0, buffer.limit()); System.out.println("收到服务端消息:" + msg); break; } } // 关闭输入流 socketChannel.shutdownInput(); // 关闭SocketChannel连接 socketChannel.close(); } }
- 先运行NioServer,再运行NioClient,运行结果:
===========NIO服务端启动============
===========NIO服务端超时等待============
===========NIO服务端超时等待============
成功连接客户端
收到客户端消息:你好,靓仔!
向客户端发送消息:你好啊!
==============NIO客户端启动================
发送消息:你好,靓仔!
收到服务端消息:你好啊!
我们在这个案例里实现了一个比较简单的Java NIO 客户端服务端通信,里面有两个小的点需要注意,注册到选择器上的通道都必须要为非阻塞模型,同时通过缓冲区传输数据时,必须要调用flip()
方法切换为读取模式。
- AioClient
/** * @Author 三分恶 * @Date 2023/5/1 * @Description AIO客户端 */ public class AioClient { public static void main(String[] args) throws Exception { // 创建异步Socket通道 AsynchronousSocketChannel client = AsynchronousSocketChannel.open(); // 异步连接服务器 client.connect(new InetSocketAddress("127.0.0.1", 8888), null, new CompletionHandler<Void, Object>() { // 创建ByteBuffer final ByteBuffer buffer = ByteBuffer.wrap(("你好,靓仔!").getBytes()); @Override public void completed(Void result, Object attachment) { // 异步发送消息给服务器 client.write(buffer, null, new CompletionHandler<Integer, Object>() { // 创建ByteBuffer final ByteBuffer readBuffer = ByteBuffer.allocate(1024); @Override public void completed(Integer result, Object attachment) { readBuffer.clear(); // 异步读取服务器发送的消息 client.read(readBuffer, null, new CompletionHandler<Integer, Object>() { @Override public void completed(Integer result, Object attachment) { readBuffer.flip(); String msg = new String(readBuffer.array(), 0, result); System.out.println("收到服务端消息:" + msg); } @Override public void failed(Throwable exc, Object attachment) { exc.printStackTrace(); try { client.close(); } catch (IOException e) { e.printStackTrace(); } } }); } @Override public void failed(Throwable exc, Object attachment) { exc.printStackTrace(); try { client.close(); } catch (IOException e) { e.printStackTrace(); } } }); } @Override public void failed(Throwable exc, Object attachment) { exc.printStackTrace(); try { client.close(); } catch (IOException e) { e.printStackTrace(); } } }); // 等待连接处理完毕 Thread.sleep(1000); // 关闭输入流和Socket通道 client.shutdownInput(); client.close(); } }
- 看下运行结果
=============AIO服务端启动=========
客户端连接成功
收到客户端消息:你好,靓仔!
收到服务端消息:你好啊!
可以看到,所有的操作都是异步进行,通过completed
接收异步回调,通过failed
接收错误回调。
而且我们发现,相较于之前的NIO
而言,AIO
其中少了Selector
选择器这个核心组件,选择器在NIO
中充当了协调者的角色。
但在Java-AIO
中,类似的角色直接由操作系统担当,而且不是采用轮询的方式监听IO
事件,而是采用一种类似于“订阅-通知”的模式。
我们也发现,虽然Java-NIO
、Java-AIO
,在性能上比Java-BIO
要强很多,但是可以看到,写法上一个比一个难搞,不过好在基本也没人直接用Java-NIO
、Java-AIO
,如果要进行网络通信,一般都会采用Netty
,它对原生的Java-NIO
进行了封装优化,接下来,我们会继续走近Netty
,敬请期待。
以上就是一文带你你搞懂Java的3种IO模型的详细内容,更多关于Java IO模型的资料请关注其它相关文章!
原文地址:https://juejin.cn/post/7231594699092492349