NIO
1. Java NIO的简介
NIO官网叫New IO 也可以叫做式非阻塞式IO NIO与原来的IO有同样的作用和目的,但是使用的方式完全不同,NIO支持面向缓冲区,基于通道的IO操作,NIO将以更高效的方式进行文件的读写操作
2.Java NIO与IO的主要区别
原来的IO是面对数据流 传统的IO是单向的 阻塞式的IO
而NIO面向缓冲区(通道)(铁路) 通道只负责运输 数据只在缓冲区 非阻塞的IO 拥有选择器
NIO简而言之:通道负责传输,,缓冲区负责存储
3.***缓冲区(Buffer)和通道(Channel)
除了Boolean之外 每个基本的数据类型都有缓冲区
ByteBuffer常用的
上述缓冲区的管理方式都是一致的 通过allocate()获取缓冲区
ByteBuffer bb = ByteBuffer.allocate(1024);
存数据的和取方法
put();
get();
flip():切换读或写方法莫模式flip 换切换以下3个属性的位置!!!
rewind():重复读取数据
clear清空缓冲区回到了最初的状态 里面的数据还在 只是移动角标位置
缓冲区中的四个核心属性
- capacity:容量,表示缓冲区中最大的存储数据的容量,一旦生命就不能改变;
- limit:界限,表示缓冲区中可以操作数据的大小(limit 后面的数据是不能进行读写的)
- position:位置,表示缓冲区中正在操作的位置。
- mark:标记 记录当前pisition的位置 通过reset恢复到mark的位置
非直接缓冲区
通过allocate()方法分配缓冲区,将缓冲区建立在JVM的内存中
数据没办法直接传输 要从磁盘写道内核地址空间 再写到用户地址空间
直接缓冲区
通过allocateDirect()方法分配直接缓冲区,将缓冲区建立在物理内存中,可以提高效率
Zero copy
一个独立的通道不占用CPU处理IO操作
/**
* 1.通道(channel):用于原节点与目标节点的连接,在javaNIO中负责缓冲区中数据传输
* channel本身不存储数据 需要配合缓冲区进行传输
* 2.通道的主要实现类
* java.nio.channels.channel
* ---FileChannel 用于本地传输
* ---SocketChaneel TCP
* ---ServerSocketChannel
* ---DatagramChannel UDP
* 3。Java支持通道提供了getChannel()方法
* 本地IO
* ---FileInputStream/FileOutputStream
* ---RandomAccessFile
* 网络IO
* Socket
* ServerSocket
* DatagramSocket
* jdk7中的nio2 在JDK7以后的nio统称NIO2
* 提供了一个静态方法open() 也可以获取通道
* 针对Files工具类的newByteChannel()
* 4.通道之间的数据传输
* transferFrom();
* transTo();
*/
public class NIOTest {
@Test
public void test() throws Exception {
//一,利用通道完成文件的复制(非直接缓冲区)
FileInputStream fis = new FileInputStream("jvm.png");
FileOutputStream fos = new FileOutputStream("1.png");
//1.获取通道
FileChannel inChannel = fis.getChannel();
FileChannel outChannel = fos.getChannel();
//2.分配一个指定大小的缓冲区
ByteBuffer buf = ByteBuffer.allocate(1024);
//3.将通道中的数据存入缓冲区中
while(inChannel.read(buf) != -1){
//4.将缓冲区中的数据再写入通道
buf.flip(); // 切换成读数据的模式
outChannel.write(buf);
buf.clear(); //清空缓冲区
}
outChannel.close();
inChannel.close();
fos.close();
fis.close();
}
@Test
public void test2() throws IOException {
//使用直接缓冲区完成复制文件
FileChannel inChannel = FileChannel.open(Paths.get("1.png"), StandardOpenOption.READ);
//CREAT_NEW 如果有报错 没有创建
FileChannel outChannel = FileChannel.open(Paths.get("2.png"), StandardOpenOption.WRITE,StandardOpenOption.READ,StandardOpenOption.CREATE_NEW);
//内存映射文件 直接缓冲区只有bytebuffer支持
MappedByteBuffer inMappedBuf = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, inChannel.size());
MappedByteBuffer outMapperBuf = outChannel.map(FileChannel.MapMode.READ_WRITE, 0, inChannel.size());
//不需要通道 直接操作缓冲区
byte[] dst = new byte[inMappedBuf.limit()];
inMappedBuf.get(dst);
outMapperBuf.put(dst);
inChannel.close();
outChannel.close();
}
@Test
public void test3() throws Exception {
//直接缓冲区的方式
FileChannel inChannel = FileChannel.open(Paths.get("1.png"), StandardOpenOption.READ);
//CREAT_NEW 如果有报错 没有创建
FileChannel outChannel = FileChannel.open(Paths.get("3.png"), StandardOpenOption.WRITE,StandardOpenOption.READ,StandardOpenOption.CREATE_NEW);
long l = inChannel.transferTo(0, inChannel.size(), outChannel);
inChannel.close();
outChannel.close();
}
}
分散与读取
分散读取 将通道的数据分散到多个缓冲区中
聚集写入 将多个缓冲区的数据聚集到一个通道中
4.文件通道(FileChannel)
5.NIO的非阻塞式通信
NIO与IO的区别 IO是阻塞式的 如果客户端发送一个读写请求 服务端如果不能够确定发送的东西是否有效 是否有数据就会发生阻塞 判断内核地址空间有没有数据
NIO式非阻塞 式相较于网络通信来说的
-
***选择器(Selector)
就是在文件中间加一个选择器 就是在准备好了的时候才通过选择器建立连接,,而不会因为服务端的不确定而出现了阻塞
//服务器端 public static void main(String[] args) throws IOException { //1.获取通道 ServerSocketChannel ssChannel = ServerSocketChannel.open(); //2.切换非阻塞模式 ssChannel.configureBlocking(false); //3.绑定连接 ssChannel.bind(new InetSocketAddress(10086)); //4.获取选择器 Selector selector = Selector.open(); //5.将通道注册到选择器上 后面的参数是选择键 监控状态的什么时间/状态 读写接收 //ACCEPT接收 指定监听事件 ssChannel.register(selector, SelectionKey.OP_ACCEPT); //6.轮询式的获取选择器上已经准备就绪的事件 while (selector.select() > 0) { Set<SelectionKey> selectionKeys = selector.selectedKeys(); //7.获取当前选择器中所有注册的选择键(已就绪的监听事件) Iterator<SelectionKey> iterator = selectionKeys.iterator(); while(iterator.hasNext()){ //8.获取准备就绪的事件 SelectionKey sk = iterator.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.取消选择键 SelectorKey iterator.remove(); } } } //客户端 public static void main(String[] args) throws IOException { //1.获取通道 SocketChannel sChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 10086)); //2.切换成非阻塞模式 sChannel.configureBlocking(false); //3.分配缓冲区 ByteBuffer buf = ByteBuffer.allocate(1024); //4.发送数据到缓冲区 Scanner scanner = new Scanner(System.in); while (scanner.hasNext()){ String str = scanner.next(); buf.put((new Date().toString()+"\n"+str).getBytes()); buf.flip(); sChannel.write(buf); buf.clear(); } //5.关闭通道 sChannel.close(); }
-
SocketChannel、ServerSocketChannel、DatagramChannel