区块链技术博客
www.b2bchain.cn

《Netty、Redis、Zookeeper高并发实战》3️⃣Java NIO通信基础详解

这篇文章主要介绍了《Netty、Redis、Zookeeper高并发实战》3️⃣Java NIO通信基础详解的讲解,通过具体代码实例进行19990 讲解,并且分析了《Netty、Redis、Zookeeper高并发实战》3️⃣Java NIO通信基础详解的详细步骤与相关技巧,需要的朋友可以参考下https://www.b2bchain.cn/?p=19990

本文实例讲述了2、树莓派设置连接WiFi,开启VNC等等的讲解。分享给大家供大家参考文章查询地址https://www.b2bchain.cn/7039.html。具体如下:

文章目录

    • 1、Java NIO(IO多路复用模型)简介
      • 1.1 NIO和OIO的对比
      • 1.2 通道(Channel)
      • 1.3 Selector选择器
      • 1.4 缓冲区(Buffer)
    • 2、详解NIO Buffer类及其属性
      • 2.1 Buffer类
      • 2.2 Buffer类的重要属性
      • 2.3 4个属性的小结
    • 3、详解NIO Buffer类的重要方法
      • 3.1 allocate()创建缓冲区
      • 3.2 put()写入到缓冲区
      • 3.3 flip()翻转
      • 3.4 get()从缓存区读取
      • 3.5 rewind()倒带
      • 3.6 mark()和reset()
      • 3.7 clear()清空缓冲区
      • 3.8 使用Buffer类的基本步骤
    • 4、详解NIO Channel(通道)类
      • 4.1 Channel(通道)的主要类型
      • 4.2 FileChannel文件通道
      • 4.3 使用FileChannel完成文件复制的实践案例
      • 4.4 SocketChannel套接字通信

        高性能的Java通信,绝对离不开Java NIO技术,现在主流的技术框架或中间件服务器,都使用了Java NIO技术,譬如Tomcat, Jetty. Netty。

1、Java NIO(IO多路复用模型)简介

        1.4之前,Java IO类库是OIO(Old IO)阻塞IO,从1.4开始Java NIO(New IO)即非阻塞IO(Non-Block IO)。

        总体上说,NIO弥补了原来面向流的OIO同步阻塞的不足,它为标准Java代码提供了告诉的、面向缓冲区的IO。

        Java NIO由以下三个核心组件组成:

  • Channel(通道)
  • Buffer(缓冲区)
  • Selector(选择器)

1.1 NIO和OIO的对比

        在Java中,NIO和OIO的区别,主要体现在三个方面:

(1) OIO是面向流(Stream Oriented)的, NIO是面向缓冲区(BufferOriented)的。

        何谓面向流,何谓面向缓冲区呢?

        OIO是面向字节流或字符流的,在一般的OIO操作中,我们以流式的方式顺序地从流(Stream)中读取一个或多个字节,因此,我们不能随意地改变读取指针的位置。而在NIO操作中则不同, NIO中引入了Channel (通道)和Buffer (缓冲区)的概念。读取和写入,只需要从通道中读取数据到缓冲区中,或将数据从缓冲区中写入到通道中。NIO不像OIO那样是顺序操作,可以随意地读取Buffer中任意位置的数据

(2) OIO的操作是阻塞的,而NIO的操作是非阻塞的。

        NIO如何做到非阻塞的呢?大家都知道, OIO操作都是阻塞的,例如,我们调用个read方法读取一个文件的内容,那么调用read的线程会被阻塞住,直到read操作完成。

        而在NIO的非阻塞模式中,当我们调用read方法时,如果此时有数据,则read读取数据并返回;如果此时没有数据,则read直接返回,而不会阻塞当前线程。NIO的非阻塞,是如何做到的呢?其实在上一章,答案已经揭晓了, NIO使用了通道和通道的多路复用技术。

(3) OIO没有选择器(Selector)概念,而NIO有选择器的概念。

        NIO的实现,是基于底层的选择器的系统调用。NIO的选择器,需要底层操作系统提供支持。而OIO不需要用到选择器。

1.2 通道(Channel)

        在OIO中,同一个网络连接会关联到两个流:一个输入流(Input Stream) ,另一个输出流(Output Stream) 。通过这两个流,不断地进行输入和输出的操作。

        在NIO中,同一个网络连接使用一个通道表示,所有的NIO的IO操作都是从通道开始的。一个通道类似于OIO中的两个流的结合体,既可以从通道读取,也可以向通道写入。

1.3 Selector选择器

什么是IO多路复用?
        指的是一个进程/线程可以同时监视多个文件描述符(一个网络连接,操作系统底层使用一个文件描述符来表示),一旦其中的一个或者多个文件描述符可读或者可写,系统内核就通知该进程/线程。

        在Java应用层面,使用Java NIO组件——Selector选择器去实现。它是一个IO事件的查询器。通过选择器,一个线程可以查询多个通道的IO事件的就绪状态。

1.4 缓冲区(Buffer)

        应用程序与通道(Channel)主要的交互操作,就是进行数据的read读取和write写入。为了完成如此大任, NIO为大家准备了第三个重要的组件-NIO Buffer (NIO缓冲区) 。通道的读取,就是将数据从通道读取到缓冲区中;通道的写入,就是将数据从缓冲区中写入到通道中。

缓冲区的使用,是面向流的OIO所没有的,也是NIO非阻塞的重要前提和基础之一

2、详解NIO Buffer类及其属性

        NIO的Buffer (缓冲区)本质上是一个内存块,既可以写入数据,也可以从中读取数据。NIO的Buffer类,是一个抽象类,位于java.nio包中,其内部是一个内存块(数组)

        NIO的Buffer与普通的内存块(Java数组)不同的是: NIO Buffer对象,提供了一组更加有效的方法,用来进行写入和读取的交替访问。

需要强调的是: Buffer类是一个非线程安全类。

2.1 Buffer类

        Buffer类是一个抽象类,对应于Java的主要数据类型,在NIO中有8种缓冲区类,别如下: ByteBuffer, CharBuffer, DoubleBuffer, FloatBuffer, IntBufferLongBuffer, ShortBuffer, MappedByteBuffer.

        前7种Buffer类型,覆盖了能在IO中传输的所有的Java基本数据类型。第8种类型MappedByteBuffer是专门用于内存映射的一种ByteBuffer类型。

2.2 Buffer类的重要属性

        Buffer列内部有一个byte[]数组内存块(内存缓冲区) ,有三个重要的成员属性: capacity(容量)、position (读写位置) 、limit (读写的限制),;来标识其读写的状态和位置。除此之外,还有一个标记属性: mark (标记) ,可以将当前的position临时存入mark中;需要的时候,可以再从mark标记恢复到position位置

1.capacity属性
        内部容量大小,初始化后不能改变,表示的不是指内存块byte[]数组的字节的数量,而是指写入的数据对象的数量。

2.position属性
        表示当前位置。与读写模式有关。

3.limit属性
        表示读写的最大上限。与读写模式有关。

2.3 4个属性的小结

《Netty、Redis、Zookeeper高并发实战》3️⃣Java NIO通信基础详解

3、详解NIO Buffer类的重要方法

        Buffer实例的获取、对Buffer实例的写入、读取、重复读、标记和重置等

3.1 allocate()创建缓冲区

《Netty、Redis、Zookeeper高并发实战》3️⃣Java NIO通信基础详解

3.2 put()写入到缓冲区

《Netty、Redis、Zookeeper高并发实战》3️⃣Java NIO通信基础详解

3.3 flip()翻转

《Netty、Redis、Zookeeper高并发实战》3️⃣Java NIO通信基础详解
《Netty、Redis、Zookeeper高并发实战》3️⃣Java NIO通信基础详解

3.4 get()从缓存区读取

缓冲区是可以重复读的。
《Netty、Redis、Zookeeper高并发实战》3️⃣Java NIO通信基础详解

        这里强调一下,在读完之后,是否可以立即进行写入模式呢?不能。现在还处于读取模式,我们必须调用Buffer.clear()或Buffer.compact(),即清空或者压缩缓冲区,才能变成写入模式,让其重新可写。

3.5 rewind()倒带

《Netty、Redis、Zookeeper高并发实战》3️⃣Java NIO通信基础详解

3.6 mark()和reset()

        Buffer.mark()方法的作用是将当前position的值保存起来,放在mark属性中,让mark属性记住这个临时位置;之后,可以调用Buffer.reset()方法将mark的值恢复到position中

        也就是说, Buffer.mark()和Buffer.reset()方法是配套使用的。两种方法都需要内部mark属性的支持。

3.7 clear()清空缓冲区

        也就是说, Buffer.mark()和Buffer.reset()方法是配套使用的。两种方法都需要内部mark属性的支持。

3.8 使用Buffer类的基本步骤

总体来说,使用Java NIO Buffer类的基本步骤如下:

        (1)使用创建子类实例对象的allocate()方法,创建一个Buffer类的实例对象。

        (2)调用put方法,将数据写入到缓冲区中。

        (3)写入完成后,在开始读取数据前,调用Buffer.flip()法,将缓冲区转换为读模式。

        (4)调用get方法,从缓冲区中读取数据。

        (5)读取完成后,调用Buffer.clear()或Buffer.compact()方法,将缓冲区转换为写入模式。

4、详解NIO Channel(通道)类

        一个连接就是用一个Channel(通道)来表示。更广泛的,一个通道可以表示一个底层的文件描述符。Java NIO的通道更加细化,对应不同的网络传输协议类型,有不同的Channel(通道)实现

4.1 Channel(通道)的主要类型

        (1) FileChannel文件通道,用于文件的数据读写。
        (2) SocketChannel套接字通道,用于Socket套接字TCP连接的数据读写。
        (3) ServerSocketChannel服务器嵌套字通道(或服务器监听通道) ,允许我们监听TCP连接请求,为每个监听到的请求,创建一个SocketChannel套接字通道。
        (4) DatagramChannel数据报通道,用于UDP协议的数据读写。

这个四种通道,涵盖了**文件IO,TCP网络、UDP IO基础IO,**下面从Channel (通道)的获取、读取、写入、关闭四个重要的操作,来对四种通道进行简单的介绍。

4.2 FileChannel文件通道

        FileChannel是专门操作文件的通道。通过FileChannel,既可以从一个文件中读取数据,也可以将数据写入到文件中。特别申明一下, FileChannel为阻塞模式,不能设置为非阻塞模式。

1.获取FileChannel通道
《Netty、Redis、Zookeeper高并发实战》3️⃣Java NIO通信基础详解

2.读取FileChannel通道
《Netty、Redis、Zookeeper高并发实战》3️⃣Java NIO通信基础详解

3.写入FileChannel通道
《Netty、Redis、Zookeeper高并发实战》3️⃣Java NIO通信基础详解

4.关闭通道
《Netty、Redis、Zookeeper高并发实战》3️⃣Java NIO通信基础详解

5.强制刷新到磁盘
《Netty、Redis、Zookeeper高并发实战》3️⃣Java NIO通信基础详解

4.3 使用FileChannel完成文件复制的实践案例

//利用通道完成文件的复制(非直接缓冲区) @Test 	public void test1(){//10874-10953 		long start = System.currentTimeMillis(); 		 		FileInputStream fis = null; 		FileOutputStream fos = null; 		//①获取通道 		FileChannel inChannel = null; 		FileChannel outChannel = null; 		try { 			fis = new FileInputStream("d:/1.mkv"); 			fos = new FileOutputStream("d:/2.mkv"); 			 			inChannel = fis.getChannel(); 			outChannel = fos.getChannel(); 			 			//②分配指定大小的缓冲区 			ByteBuffer buf = ByteBuffer.allocate(1024); 			 			//③将通道中的数据存入缓冲区中 			while(inChannel.read(buf) != -1){ 				buf.flip(); //切换读取数据的模式 				//④将缓冲区中的数据写入通道中 				outChannel.write(buf); 				buf.clear(); //清空缓冲区 			} 		} catch (IOException e) { 			e.printStackTrace(); 		} finally { 			if(outChannel != null){ 				try { 					outChannel.close(); 				} catch (IOException e) { 					e.printStackTrace(); 				} 			} 			 			if(inChannel != null){ 				try { 					inChannel.close(); 				} catch (IOException e) { 					e.printStackTrace(); 				} 			} 			 			if(fos != null){ 				try { 					fos.close(); 				} catch (IOException e) { 					e.printStackTrace(); 				} 			} 			 			if(fis != null){ 				try { 					fis.close(); 				} catch (IOException e) { 					e.printStackTrace(); 				} 			} 		} 		 		long end = System.currentTimeMillis(); 		System.out.println("耗费时间为:" + (end - start)); 		 	}  
//通道之间的数据传输(直接缓冲区) 	@Test 	public void test3() throws IOException{ 		FileChannel inChannel = FileChannel.open(Paths.get("d:/1.mkv"), StandardOpenOption.READ); 		FileChannel outChannel = FileChannel.open(Paths.get("d:/2.mkv"), StandardOpenOption.WRITE, StandardOpenOption.READ, StandardOpenOption.CREATE); 		 //		inChannel.transferTo(0, inChannel.size(), outChannel); 		outChannel.transferFrom(inChannel, 0, inChannel.size()); 		 		inChannel.close(); 		outChannel.close(); 	} 

4.4 SocketChannel套接字通信

        无论是ServerSocketChannel,还是SocketChannel,都支持阻塞和非阻塞两种模式。如何进行模式的设置呢?调用configureBlocking方法,具体如下:

(1) socketChannel.configureBlocking (false)设置为非阻塞模式。
(2) socketChannel.configureBlocking (true)设置为阻塞模式。

1.获取SocketChannel传输通道
《Netty、Redis、Zookeeper高并发实战》3️⃣Java NIO通信基础详解

2.读取SocketChannel传输通道
3.写入到SocketChannel传输通道
4.关闭SocketChannel传输通道

 import org.junit.Test;  /*  * 一、使用 NIO 完成网络通信的三个核心:  *   * 1. 通道(Channel):负责连接  * 		  * 	   java.nio.channels.Channel 接口:  * 			|--SelectableChannel  * 				|--SocketChannel  * 				|--ServerSocketChannel  * 				|--DatagramChannel  *   * 				|--Pipe.SinkChannel  * 				|--Pipe.SourceChannel  *   * 2. 缓冲区(Buffer):负责数据的存取  *   * 3. 选择器(Selector):是 SelectableChannel 的多路复用器。用于监控 SelectableChannel 的 IO 状况  *   */ public class TestNonBlockingNIO { 	 	//客户端 	@Test 	public void client() throws IOException{ 		//1. 获取通道 		SocketChannel sChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9898)); 		 		//2. 切换非阻塞模式 		sChannel.configureBlocking(false); 		 		//3. 分配指定大小的缓冲区 		ByteBuffer buf = ByteBuffer.allocate(1024); 		 		//4. 发送数据给服务端 		Scanner scan = new Scanner(System.in); 		 		while(scan.hasNext()){ 			String str = scan.next(); 			buf.put((new Date().toString() + "n" + str).getBytes()); 			buf.flip(); 			sChannel.write(buf); 			buf.clear(); 		} 		 		//5. 关闭通道 		sChannel.close(); 	}  	//服务端 	@Test 	public void server() throws IOException{ 		//1. 获取通道 		ServerSocketChannel ssChannel = ServerSocketChannel.open(); 		 		//2. 切换非阻塞模式 		ssChannel.configureBlocking(false); 		 		//3. 绑定连接 		ssChannel.bind(new InetSocketAddress(9898)); 		 		//4. 获取选择器 		Selector selector = Selector.open(); 		 		//5. 将通道注册到选择器上, 并且指定“监听接收事件” 		ssChannel.register(selector, SelectionKey.OP_ACCEPT); 		 		//6. 轮询式的获取选择器上已经“准备就绪”的事件 		while(selector.select() > 0){ 			 			//7. 获取当前选择器中所有注册的“选择键(已就绪的监听事件)” 			Iterator<SelectionKey> it = selector.selectedKeys().iterator(); 			 			while(it.hasNext()){ 				//8. 获取准备“就绪”的是事件 				SelectionKey sk = it.next(); 				 				//9. 判断具体是什么事件准备就绪 				if(sk.isAcceptable()){ 					//10. 若“接收就绪”,获取客户端连接 					SocketChannel sChannel = ssChannel.accept(); 					 					//11. 切换非阻塞模式 					sChannel.configureBlocking(false); 					 					//12. 将该通道注册到选择器上 					sChannel.register(selector, SelectionKey.OP_READ); 				}else if(sk.isReadable()){ 					//13. 获取当前选择器上“读就绪”状态的通道 					SocketChannel sChannel = (SocketChannel) sk.channel(); 					 					//14. 读取数据 					ByteBuffer buf = ByteBuffer.allocate(1024); 					 					int len = 0; 					while((len = sChannel.read(buf)) > 0 ){ 						buf.flip(); 						System.out.println(new String(buf.array(), 0, len)); 						buf.clear(); 					} 				} 				 				//15. 取消选择键 SelectionKey 				it.remove(); 			} 		} 	} }  

本文转自互联网,侵权联系删除《Netty、Redis、Zookeeper高并发实战》3️⃣Java NIO通信基础详解

赞(0) 打赏
部分文章转自网络,侵权联系删除b2bchain区块链学习技术社区 » 《Netty、Redis、Zookeeper高并发实战》3️⃣Java NIO通信基础详解
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

b2b链

联系我们联系我们