NIO

1、简介

1.1 Java中的IO介绍

  1. BIO:BlockingIO,同步式阻塞式IO,即传统的IO,是java中最早期的流
  2. NIO:Non-BlockingIO,又称New IO,同步式非阻塞IO,是JDK1.4提供的流
  3. AIO:AsynchronousIO,异步是非阻塞IO,可以认为是NIO的二代版本,是JDK1.8提供的流

1.2 概述

  1. NIO是JDK1.4出现的一个新的用于进行数据传输的流
  2. 全称是Non-BlockingIO,是一种同步式非阻塞式的IO,也是一种能供进行多路复用的IO
  3. NIO中有3大组件:Buffer、Channel、Selector
  4. NIO在使用的时候可以基于事件驱动方式来实现

1.3 BIO的缺点

  1. 一对一的连接方式:即每一个连接请求对应一个线程,在请求最大的情况下,会导致服务器端的压力非常大而致整个服务器的效率变低
  2. 阻塞:当线程在进行read或者write的时候,除非读完或者写完,否则在这个过程中不能发生任何操作
  3. 单项传输:数据只能从一端传向另一端,如果需要反向传输需要令创建流对象

1.4 NIO的特点

  1. 一对多的连接方式:利用一个或者少量线程处理大量的连接请求,降低服务器端的压力
  2. 非阻塞:在线程不能进行read或者write方法的时候,立即返回0,等待下一次操作
  3. 双向传输:利用通道可以实现数据的双向

1.5 NIO的缺点

  1. 在请求量比较大的情况下会出现部分请求的响应时间比较长的现象
  2. 不适用于长任务场景,不然会导致其他的请求无法处理

1.6 BIO和NIO的比较

2. ByteBuffer

2.1 概述

  1. 字节缓冲区,继承了Buffer类
  2. 底层是依靠字节数组来存储数据
  3. 本身是一个抽象类,需要利用其子类创建对象或者是利用其提供的allocate或者wrap方法来创建ByteBuffer对象
  4. 重要位置:capacity >= limit >= position >=mark

2.2 重要位置

  1. capacity:容量位,用于标记该缓冲区的容量,在缓冲区创建好之后就不再改变
  2. limit:限制位,用于限制操作位position所能达到的最大位置。在缓冲区刚创建的时候指向容量位
  3. position:操作位,用于指向要操作的位置,实际意义类似于数组中的下标,在缓冲区刚创建的时候指向0
  4. mark:标记位,用于进行标记,在缓冲区刚创建的时候指向-1,默认不启用

2.3 重要方法

2.4 示例

Demo01
1package cn.tedu.buffer; 2 3import java.nio.ByteBuffer; 4 5public class ByteBufferDemo { 6 7 public static void main(String[] args) { 8 9 // 表示给底层的字节数组来指定大小 10 // 缓冲区在给定之后,长度就不能改变了 11 ByteBuffer buffer = 12 ByteBuffer.allocate(10); 13 // 添加数据 14 buffer.put("abc".getBytes()); 15 buffer.put((byte) 0); 16 buffer.put("def".getBytes()); 17 // 将position挪动 18 // buffer.position(0); 19 // 获取元素 20 // 获取的是一个字节 21 // byte b = buffer.get(); 22 // System.out.println(b); 23 // byte b2 = buffer.get(); 24 // System.out.println(b2); 25 26 // 遍历 27 // 将limit挪到position的位置上 28 // 将position归零 29 // buffer.limit(buffer.position()); 30 // buffer.position(0); 31 // 上述两部操作称之为翻转缓冲区 32 // 等价于 33 buffer.flip(); 34 // while(buffer.position() < buffer.limit()){ 35 // 等价 36 while(buffer.hasRemaining()){ 37 byte b = buffer.get(); 38 System.out.println(b); 39 } 40 41 } 42 43}
Demo02
1package cn.tedu.buffer; 2 3import java.nio.ByteBuffer; 4 5public class ByteBufferDemo2 { 6 7 public static void main(String[] args) { 8 9 // 适合于数据未知的场景 10 // ByteBuffer buffer = ByteBuffer.allocate(); 11 // 数据已知 12 // ByteBuffer buffer = 13 // ByteBuffer.wrap("hello big2002".getBytes()); 14 // System.out.println(buffer.position()); 15 16 ByteBuffer buffer = ByteBuffer.allocate(10); 17 buffer.put("hello".getBytes()); 18 19 // 获取缓冲区底层的字节数组 20 byte[] data = buffer.array(); 21 // System.out.println(new String(data, 0, 22 // buffer.position())); 23 buffer.flip(); 24 System.out.println(new String(data, 0, 25 buffer.limit())); 26 27 } 28 29}

3. Channel

3.1 简介

  1. Channel,称之为通道,在NIO中用于完成数据的传输
  2. 在操作的时候是面向缓冲区进行的
  3. 可以实现数据的双向传输
  4. Channel默认是阻塞的,可以手动设置为非阻塞

3.2 FileChannel

3.2.1 概述

  1. FileChannel,顾名思义是面向文件的通道
  2. 可以利用FileChannel完成对文件的读写操作
  3. 利用FileChannel读取文件的时候,是先将文件中的内容映射到虚拟内存中,然后在读取到程序的缓冲区中
  4. FileChannel不能直接创建,可以利用FileInputStream、FileOutPutStream、RandomAccessFile对象中的个体Channel()方法获取
  5. 如果是通过FileInputStream获取FileChannel,那么只能进行读取操作
  6. 如果是通过FileOutputStream获取FileChannel,那么只能进行写入操作
  7. 如果是通过RandomAccessStream获取FileChannel,那么可以进行读写操作

3.2.2 示例

读取过程
1@Test 2public void readFile() throws Exception { 3 4 // 创建RandomAccessFile对象。指定模式为读写模式 5 RandomAccessFile raf = new RandomAccessFile("F:\\a.txt", "rw"); 6 // 获取FileChannel对象 7 FileChannel fc = raf.getChannel(); 8 // 创建缓冲区用于存储数据 9 ByteBuffer buffer = ByteBuffer.allocate(10); 10 // 记录读取的字节个数 11 int len; 12 // 读取数据 13 while ((len = fc.read(buffer)) != -1) { 14 System.out.println(new String(buffer.array(), 0, len)); 15 buffer.flip(); 16 } 17 // 关流 18 raf.close(); 19 20}
写入过程
1@Test 2public void writeFile() throws Exception { 3 4 // 创建RandomAccessFile对象。指定模式为读写模式 5 RandomAccessFile raf = new RandomAccessFile("F:\\test.txt", "rw"); 6 // 获取FileChannel对象 7 FileChannel fc = raf.getChannel(); 8 // 创建缓冲区,并且将数据放入缓冲区 9 ByteBuffer src = ByteBuffer.wrap("hello".getBytes()); 10 // 利用通道写出数据 11 fc.write(src); 12 // 关流 13 raf.close(); 14 15} 16
复制文件
1@Test 2public void copyFile() throws Exception { 3 4 // 创建流对象指向对应的文件 5 FileInputStream in = new FileInputStream("F:\\a.txt"); 6 FileOutputStream out = new FileOutputStream("E:\\a.txt"); 7 8 // 获取FileChannel对象 9 FileChannel src = in.getChannel(); 10 FileChannel dest = out.getChannel(); 11 12 // 准备缓冲区 13 ByteBuffer buffer = ByteBuffer.allocate(10); 14 // 读取数据,将读取到的数据写出 15 while (src.read(buffer) != -1) { 16 buffer.flip(); 17 dest.write(buffer); 18 buffer.clear(); 19 } 20 // 关流 21 in.close(); 22 out.close(); 23 24}

3.3 UDP

3.3.1 概述

  1. 用于进行UDP收发的通道
  2. 是无连接的网络协议,只能进行发送和接受的操作
  3. 基本类是DatagramChannel,是一个抽象类

3.3.2 示例

发送端
1@Test 2public void send() throws IOException { 3 // 开启通道 4 DatagramChannel dc = DatagramChannel.open(); 5 // 准备数据 6 ByteBuffer buffer = ByteBuffer.wrap("hello".getBytes()); 7 // 发送数据 8 dc.send(buffer, new InetSocketAddress("localhost", 8090)); 9 // 关闭通道 10 dc.close(); 11}
接收端
1@Test 2public void recieve() throws IOException { 3 4 // 开启通道 5 DatagramChannel dc = DatagramChannel.open(); 6 // 绑定连接地址和端口号 7 dc.socket().bind(new InetSocketAddress(8090)); 8 9 // 准备缓冲区 10 ByteBuffer buffer = ByteBuffer.allocate(1024); 11 // 接收数据 12 dc.receive(buffer); 13 System.out.println(new String(buffer.array(), 0, buffer.position())); 14 15 // 关闭通道 16 dc.close(); 17 18}

3.4 TCP

3.4.1 概述

  1. 用于进行TCP通信的通道
  2. 需要进行连接的网络协议
  3. 提供了连接、接收、读取、写入操作
  4. 客户端通道是SocketChannel,服务器端通道是ServerSocketChannel

3.4.2 示例

客户端
1@Test 2public void client() throws IOException { 3 4 // 开启客户端通道 5 SocketChannel sc = SocketChannel.open(); 6 // 可以手动设置为非阻塞模式 7 sc.configureBlocking(false); 8 // 发起连接 9 sc.connect(new InetSocketAddress("localhost", 8090)); 10 // 由于是非阻塞的,所以连接不一定建立了,所以要判断连接是否建立 11 while (!sc.isConnected()) 12 // 如果连接没有建立,则试图重新建立连接 13 sc.finishConnect(); 14 // 写出数据 15 sc.write(ByteBuffer.wrap("hello".getBytes())); 16 // 关闭通道 17 sc.close(); 18}

服务器端

1@Test 2public void server() throws IOException { 3 4 // 开启服务器端通道 5 ServerSocketChannel ssc = ServerSocketChannel.open(); 6 // 绑定要监听的端口 7 ssc.socket().bind(new InetSocketAddress(8090)); 8 // 手动设置为非阻塞 9 ssc.configureBlocking(false); 10 // 获取连接过来的通道 11 SocketChannel sc = ssc.accept(); 12 // 判断连接是否真正建立 13 while (sc == null) 14 // 如果没有建立则重新获取建立 15 sc = ssc.accept(); 16 // 准备缓冲区用于存储数据 17 ByteBuffer buffer = ByteBuffer.allocate(1024); 18 // 读取数据 19 sc.read(buffer); 20 System.out.println(new String(buffer.array(), 0, buffer.position())); 21 // 关闭通道 22 ssc.close(); 23 24}

4. Selector

4.1 概述

  1. Selector,称之为多路复用选择器
  2. 对通道进行选择,需要基于事件进行驱动
  3. 针对了四类事件:connect、accept、read、write,四类事件定义在SelectionKey中
  4. 可以实现利用一个或者少量线程处理大量请求
  5. 适用于大量的短任务场景,不适用于长任务场景
  6. Selector针对的必须是非阻塞的通道

4.2 示例

作用:针对通道的指定事件来进行选择
Selector在使用的时候针对非阻塞通道进行操作

客户端

1package cn.tedu.selector; 2 3 4import java.io.IOException; 5import java.net.InetSocketAddress; 6import java.nio.ByteBuffer; 7import java.nio.channels.SocketChannel; 8 9public class Client { 10 public static void main(String[] args) throws IOException { 11 12 SocketChannel sc = SocketChannel.open(); 13 sc.connect(new InetSocketAddress("localhost",8090)); 14 sc.write(ByteBuffer.wrap("hello server".getBytes())); 15 //读取数据 16 ByteBuffer buffer = ByteBuffer.allocate(1024); 17 sc.read(buffer); 18 System.out.println( 19 new String(buffer.array(),0,buffer.position())); 20 21 } 22}

服务器端

1package cn.tedu.selector; 2 3import java.io.IOException; 4import java.net.InetSocketAddress; 5import java.nio.ByteBuffer; 6import java.nio.channels.*; 7import java.util.Iterator; 8import java.util.Set; 9 10public class Srever { 11 public static void main(String[] args) throws IOException { 12 13 //开启服务器通道 14 ServerSocketChannel ssc = ServerSocketChannel.open(); 15 //设置非阻塞 16 ssc.configureBlocking(false); 17 //绑定端口 18 ssc.bind(new InetSocketAddress(8090)); 19 //开启选择器 20 Selector selc = Selector.open(); 21 //将通道注册到选择器上 22 ssc.register(selc, SelectionKey.OP_ACCEPT); 23 24 //模拟:服务器开启之后不关闭 25 while(true){ 26 //随着运行时间的延长,接收到的请求会越来越多 27 //需要针对这些请求来进行选择,将能触发时间的请求留下 28 //将不能触发事件的请求过滤掉 29 selc.select(); 30 //选完之后能够留下来的请求都是有用的请求 31 //connect/read/write 32 //获取请求的事件类型 33 Set<SelectionKey> set = selc.selectedKeys(); 34 //需要针对请求的不同类型类进行分门别类的处理 35 Iterator<SelectionKey> it = set.iterator(); 36 while(it.hasNext()){ 37 SelectionKey key = it.next(); 38 //触发服务器的accept操作 - 说明客户端一定调用了connect方法 39 if (key.isAcceptable()){ 40 //从事件中获取通道 41 ServerSocketChannel sscx = (ServerSocketChannel) key.channel(); 42 //接收连接 43 SocketChannel sc = sscx.accept(); 44 sc.configureBlocking(false); 45 //根据需求确定,如果需要读操作,那么就给READ 46 //如果需要写操作,那就给WRITE 47 //如果存在多个register,那么后边的会覆盖前边的 48 sc.register(selc, 49 // SelectionKey.OP_READ + SelectionKey.OP_WRITE); 50 // SelectionKey.OP_READ | SelectionKey.OP_WRITE); 51 SelectionKey.OP_READ ^ SelectionKey.OP_WRITE); 52 } 53 if (key.isReadable()){ 54 SocketChannel sc = (SocketChannel)key.channel(); 55 //读数据 56 ByteBuffer buffer = ByteBuffer.allocate(1024); 57 sc.read(buffer); 58 System.out.println(new String(buffer.array(),0,buffer.position())); 59 //读取完成之后,需要将READ事件从通道身上移除掉 60 //key.interestOps() 获取到所有事件 61 sc.register(selc, 62 key.interestOps() - SelectionKey.OP_READ); 63// key.interestOps() ^ SelectionKey.OP_READ); 64 } 65 if (key.isWritable()){ 66 SocketChannel sc = (SocketChannel) key.channel(); 67 sc.write(ByteBuffer.wrap("收到数据啦~~".getBytes())); 68 sc.register(selc, 69 key.interestOps() - SelectionKey.OP_WRITE); 70 } 71 //处理完成之后,需要将这一大类时间移除掉 72 it.remove(); 73 } 74 } 75 } 76}
点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

Java NIO和IO的区别

IO               NIO面向流           面向缓冲阻塞IO           非阻塞IO无               选择器面向流与面向缓冲JavaNIO和IO之间第一个最大的区别是,IO是面向流的,NIO是面向缓冲区的。JavaIO面向流意味着每次从流中

BIO、NIO、AIO、多路复用IO的区别(图解)

原文地址:blog.csdn.net/lzb348110175/article/details/98941378学习之前,我们先来了解一下IO模型:    ①同步阻塞IO(BlockingIO):即传统的IO模型。    ②同步非阻塞IO(NonblockingIO):默认创建的socket都是阻塞的,非阻塞