Java NIO中的阻塞与非阻塞IO

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_27092581/article/details/78440261

一、什么是阻塞和非阻塞?

传统的 IO 流都是阻塞式的。也就是说,当一个线程调用 read() 或 write() 时,该线程被阻塞,直到有一些数据被读取或写入,

该线程在此期间不 能执行其他任务。因此,在完成网络通信进行 IO 操作时,由于线程会 阻塞,所以服务器端必须为每个客户端都

提供一个独立的线程进行处理, 当服务器端需要处理大量客户端时,性能急剧下降。

 Java NIO 是非阻塞模式的。当线程从某通道进行读写数据时,若没有数 据可用时,该线程可以进行其他任务。

线程通常将非阻塞 IO 的空闲时 间用于在其他通道上执行 IO 操作,所以单独的线程可以管理多个输入 和输出通道。

因此,NIO 可以让服务器端使用一个或有限几个线程来同 时处理连接到服务器端的所有客户端。

二、NIO中阻塞和非阻塞用法

1、NIO中的阻塞式IO用法

这里通过socket网络建立client和server实现图片上传为例:

客户端代码:

public void client()throws Exception
	{
		//建立网络通道,连接指定服务器端口
				SocketChannel socketChannel=SocketChannel.open(new InetSocketAddress("127.0.0.1", 10011));
				//创建缓冲区
				ByteBuffer byteBuffer=ByteBuffer.allocate(1024);
				//创建客户端本地文件通道
				FileChannel fileChannel=FileChannel.open(Paths.get("sb.jpg"), StandardOpenOption.READ);
				//读取本地文件,发送到服务器
				while(fileChannel.read(byteBuffer)!=-1)
				{
					byteBuffer.flip();
					socketChannel.write(byteBuffer);
					byteBuffer.clear();
				}
				//告诉服务端我发送完了
				socketChannel.shutdownOutput();
				
				//接受端反馈
				int len=0;
				while((len=socketChannel.read(byteBuffer))!=-1)
				{
					byteBuffer.flip();
					//直接打印
					System.out.println(new String(byteBuffer.array(),0,len));
					byteBuffer.clear();
				}
				//关闭资源
				fileChannel.close();
				socketChannel.close();
	}

服务端代码:

public void Server()throws Exception
		{
			//获取服务器通道
			ServerSocketChannel serverSocketChannel=ServerSocketChannel.open();
			//建立指向服务器的通道
			FileChannel fileChannel=FileChannel.open(Paths.get("xxx.jpg"), StandardOpenOption.WRITE,StandardOpenOption.READ,StandardOpenOption.CREATE);
			//绑定连接 指定端口
			serverSocketChannel.bind(new InetSocketAddress(10011));
			//可以理解为连接客户端的通道
			SocketChannel socketChannel=serverSocketChannel.accept();
			//建立一个缓冲区
			ByteBuffer buf=ByteBuffer.allocate(1024);
			while(socketChannel.read(buf)!=-1)
			{
				buf.flip();
				fileChannel.write(buf);
				buf.clear();
			}
			//发送反馈给客户端
			buf.put("服务端接受数据成功!".getBytes());
			//切换可读
			buf.flip();
			socketChannel.write(buf);
			//关闭通道
			socketChannel.close();
			fileChannel.close();
			serverSocketChannel.close();
		}

2、NIO中的非阻塞式IO的使用

要用NIO中的非阻塞式IO就需要了解NIO中的Selector,中文意思“选择器”。

我们需要知道,通过Selector可以使一个线程管理多个Channel。

客户端:

public void client()throws Exception
	{
		SocketChannel socketChannel=SocketChannel.open(new InetSocketAddress("127.0.0.1", 10010));
		//切换非阻塞式IO
		socketChannel.configureBlocking(false);
		//分配缓冲区
		ByteBuffer buf=ByteBuffer.allocate(1024);
		buf.put(new Date().toString().getBytes());
		buf.flip();
		socketChannel.write(buf);
		buf.clear();
		socketChannel.close();
	}

服务端:

public void Server()throws Exception
	{
		ServerSocketChannel serverSocketChannel=ServerSocketChannel.open();
		//切换为非阻塞
		serverSocketChannel.configureBlocking(false);
		//绑定端口
		serverSocketChannel.bind(new InetSocketAddress(10010));
		//获取选择器
		Selector selector	=Selector.open();
		//将通道注册到选择器上 第二个参数用于监控通道的什么状态
		//指定为了接收监听事件
		serverSocketChannel.register(selector,SelectionKey.OP_ACCEPT );
		//通过选择器轮询获取选择器上已经准备就绪的事件
		while(selector.select()>0)
		{
			//获取当前选择器中所有注册的选择键(已就绪监听键)
			Iterator<SelectionKey> iterator=selector.selectedKeys().iterator();
			//遍历
			while(iterator.hasNext())
			{
				//获取准备就绪的事件
				SelectionKey key=iterator.next();
				//判断是什么事件准备就绪
				//1、接受就绪 那就获取客户端的连接
				if(key.isAcceptable())
				{
					//建立连接
					SocketChannel socketChannel=serverSocketChannel.accept();
					//设置为非阻塞
					socketChannel.configureBlocking(false);
					//注册读监听
					socketChannel.register(selector, SelectionKey.OP_READ);
				}
				//读就绪
				else if(key.isReadable())
				{
					SocketChannel sChannel=(SocketChannel)key.channel();
					//读取数据
					ByteBuffer buf=ByteBuffer.allocate(1024);
					int len=0;
					while((len=sChannel.read(buf))!=-1)
					{
						buf.flip();
						System.out.println(new String(buf.array(),0,len));
						buf.clear();
					}
				}
				//取消选择键
				iterator.remove();
			}
		}
		//关闭资源
	}


猜你喜欢

转载自blog.csdn.net/qq_27092581/article/details/78440261