NIO:
Jdk 1.4+ New IO 面向通道和缓冲区
所在包:java.nio
执行流程:
数据总数由通道写入到buffer , 或者是从buffer写入通道
完全替换IO(面向流 单向的)
三个组件:
1. channel 通道
2. Buffer 缓冲区
3. Selector 选择器
NIO和IO 的区别
1. 传统的IO面向流 ,NIO面向缓冲区
2. 传统的IO是阻塞IO ,NIO是非阻塞IO(可并行,,可占位)
3. NOI增加了新功能
① 由选择器
② 可以使用正则表达式
③ 支持内存映射(计算快,效率快)
④ 支持文件锁
一:buffer 缓冲区
读写两种模式
本质上就是一个数据集 数组?集合?
本质是一个可以写入数据,并且从中读取数据的内存!!!
存储的是相同数据类型的数据集
三个重要的值:
1. Position:写入或者读取的数据的当前指针
2. Limit:有多少数据可以写或者可以读
3. Capacity:缓冲区的最大容量
在写(write)模式的情况下
limit 和 capacity 值一致
Position 最大 值{下标(0开始)}是capacity-1
写到哪 值是什么 从0开始
指针的值是 真实值+1 --> 将要写的位置 (最大到capacity值)
xxxBuffer buffer = xxxBuffer.allocate(最大容量);
Buffer.put(xx); 写入数据
在读(read)模式的情况下
Position 读到那值值是几,,但从0开始
Limit 的值是position写模式的值(可读数据)
重设缓冲区 切换到读模式
Buffer.flip();
小Tip:
package com.fsdm.nio.buffer; import java.nio.IntBuffer; /** * @author 房上的猫 * @create 2018-07-03 17:11 * @博客地址: https://www.cnblogs.com/lsy131479/ * <p> * NIO 初探缓冲区 **/ public class BufferTest { public static void main(String[] args) { //创建缓冲区实例 IntBuffer buffer = IntBuffer.allocate(10); System.out.println( " =====================写操作==================== "); //监控各值 System.out.println( "*************** 写模式初始值 ***************"); System.out.println( "capacity=== » " +buffer.capacity()); System.out.println( "position=== » " +buffer.position()); System.out.println( "limit===» "+buffer. limit()); //写入数据 buffer.put(new int[]{1,1,1,2}); //监控各值 System.out.println( "*************** 写入值后 ***************"); System.out.println( "capacity=== » " +buffer.capacity()); System.out.println( "position=== » " +buffer.position()); System.out.println( "limit===» "+buffer. limit()); //重设缓冲区 切换到读模式 buffer.flip(); System.out.println( " ====================读操作===================== "); //监控各值 System.out.println( "*************** 读模式初始值 ***************"); System.out.println( "capacity=== » " +buffer.capacity()); System.out.println( "position=== » " +buffer.position()); System.out.println( "limit===» "+buffer. limit()); //简单的读操作 while (buffer.hasRemaining()){ System.out.println(buffer.get()); //监控各值 System.out.println( "*************** 读取中 ***************"); System.out.println( "capacity=== » " +buffer.capacity()); System.out.println( "position=== » " +buffer.position()); System.out.println( "limit===» "+buffer. limit()); } } }
二:channel 管道/通道
作用:
1. 基于buffer(缓冲区)对数据进行读写
2. 管道是双向的,流是单向的
3. 可以异步的读写
常用实现类:
网络传输:
UDP:面向非连接,无脑流,效率高,性能好,非安全
TCP:面向连接,效率低,性能差,安全
1. FileChannel:从文件中读写数据
2. DataGrarmChannel:通过UDP来读写网络中数据
3. SocketChannel:通过TCP读写网络中的数据
4. ServerSocketChannel:可以监听新来的TCP连接,每进来一个,都会创建一个新的 SocketChannel
小Tip:
package com.fsdm.nio.channel; import java.io.*; import java.nio.ByteBuffer; import java.nio.channels.Channel; import java.nio.channels.FileChannel; /** * @author 房上的猫 * @create 2018-07-05 14:09 * @博客地址: https://www.cnblogs.com/lsy131479/ * <p> * 通过管道向文件中读写数据 **/ public class ChannelDemo { public static void main(String[] args) { //准备数据 String[] strs = {"haha","hehe","heihei"}; //写入 文件 输出流 FileOutputStream fos=null; //准备管道 FileChannel channel = null; try { fos = new FileOutputStream("f:/a.txt"); //获取管道数据 channel = fos.getChannel(); //准备缓冲区 ByteBuffer buffer = ByteBuffer.allocate(1024); //将事先准备好的数据 写入缓冲区 for (String str:strs) { buffer.put(str.getBytes()); buffer.put(" ".getBytes()); } //将缓存区切换到读模式 buffer.flip(); //将缓冲区数据读取出来并写入磁盘 真正的写 channel.write(buffer); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }finally { //回收资源 try { channel.close(); fos.close(); } catch (IOException e) { e.printStackTrace(); } } } }
练习实例:(利用管道将a文件内容复制到b文件):
package com.fsdm.nio.channel; import java.io.*; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; /** * @author 房上的猫 * @create 2018-07-05 15:02 * @博客地址: https://www.cnblogs.com/lsy131479/ * <p> * a.txt --> b.txt **/ public class ChannelBuffer { public static void main(String[] args) { //准备起始文件与终止文件 File inFile = new File("f:/a.txt"); File outFile = new File("f:/b.txt"); //准备输入输出流 FileInputStream fis = null; FileOutputStream fos = null; //准备双向管道 FileChannel inChannel = null; FileChannel outChannel = null; try { //实例化各对象 fis = new FileInputStream(inFile); fos = new FileOutputStream(outFile); inChannel = fis.getChannel(); outChannel = fos.getChannel(); //准备缓存区 (作为中转站) ByteBuffer buffer = ByteBuffer.allocate(1024); int num = 0; //写入到缓冲区 while ((num=inChannel.read(buffer))!=-1){ //转换缓冲区模式 buffer.flip(); //读取缓冲区数据并写入到磁盘 outChannel.write(buffer); //清空缓冲区 方便下次读写 buffer.clear(); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }finally { try { inChannel.close(); fis.close(); outChannel.close(); fos.close(); } catch (IOException e) { e.printStackTrace(); } } } }
三:selector 选择器
待后期单独总结
高并发:NIO,线程
Java 运行时数据区
运行时都会被创建
共享数据:
堆 heap
方法区 method area
私有数据:
虚拟机栈 vm stack
本地方法栈 native method stack
程序计数器
Xms:初始化容量
Xmx:最大容量
内存映射:
就是把文件映射到电脑中的内存中,通过操作内存从而打到操作文件的目的
内存中操作速度是最快的
Java 中读取文件的几种方式:
1. RandomAceessFile 随机读取,速度最慢
2. FileInputStream 流的方式读取
3. BufferReader 缓存的方式读取
4. MappedByteBuffer 内存映射,速度最快
内存映射的三种模式:MapMode
1. READ_ONLY :对缓冲区的内存只读
2. READ_WRITE :对缓冲区的内存读写
3. PRIVATE :只会对缓冲区的内存进行修改,不会影响到真实的文件
通常适用于数据的读取,一般不会进行对数据的写入
内存映射读取文件与普通读取文件 效率对比:
package com.fsdm.nio; import java.io.FileNotFoundException; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; /** * @author 房上的猫 * @create 2018-07-05 18:00 * @博客地址: https://www.cnblogs.com/lsy131479/ * <p> * 内存映射 **/ public class MapperDemo { public static void main(String[] args) { FileChannel channel = null; RandomAccessFile file = null; try { file = new RandomAccessFile("e:/struts-2.3.31-lib.zip","rw"); //获取通道 channel = file.getChannel(); //创建内存映射对象 MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_ONLY,0,channel.size()); byte[] bytes = new byte[1024]; //获取文件大小 long length = file.length(); long begin = System.currentTimeMillis(); ByteBuffer buffer2 = ByteBuffer.allocate(1024); for (int i=0;i<length;i+=1024){ if (length-i>1024){ buffer2=buffer.get(bytes); }else{ buffer2=buffer.get(new byte[(int)(length-i)]); } buffer2.flip(); buffer2.clear(); } long end = System.currentTimeMillis(); System.out.println(end-begin); System.out.println("================"); begin = System.currentTimeMillis(); //普通读取缓冲区 ByteBuffer buffer1 = ByteBuffer.allocate(1024); while (channel.read(buffer1)!=-1){ buffer1.flip(); buffer.clear(); } end = System.currentTimeMillis(); System.out.println(end-begin); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }
文件锁:
FileLock :基于FlieChannel对文件提供锁的功能
共享锁:
共享读的操作
读可以有多个,但是只能有一个人在写
适合读取数据
目的:是为了防止其他线程拿到独占锁
独占锁:
只能有一个读或写
读写不能同时
适合写数据
Lock():
阻塞
无参默认是独占锁
有参的可设置锁状态
TyLock():
非阻塞
小Tip 玩玩?:
package com.fsdm.nio.lock; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.channels.FileLock; import java.nio.channels.OverlappingFileLockException; /** * @author 房上的猫 * @create 2018-07-05 18:15 * @博客地址: https://www.cnblogs.com/lsy131479/ * <p> * 锁 **/ public class LockDemo implements Runnable { static RandomAccessFile file = null; static FileChannel channel = null; static FileLock lock = null; public static void main(String[] args) { Thread thread = null; try { // lock = channel.lock(0L, Long.MAX_VALUE, true); } catch (Exception e) { e.printStackTrace(); } for (int i = 0; i < 10; i++) { try { file = new RandomAccessFile("f:/a.txt", "rw"); channel = file.getChannel(); if (i==0){ lock = channel.lock(); // lock = channel.lock(0L, Long.MAX_VALUE, true); buffer.put("xx".getBytes()); } } catch (Exception e) { e.printStackTrace(); } LockDemo lockDemo = new LockDemo(); thread = new Thread(lockDemo, i+":"); thread.start(); } try { System.out.println(Thread.currentThread().getName()+(char)( channel.write(buffer))); } catch (IOException e) { e.printStackTrace(); } ; } static ByteBuffer buffer = ByteBuffer.allocate(1024); @Override public void run() { try { buffer =ByteBuffer.allocate(1024); buffer.put("xx".getBytes()); System.out.println(Thread.currentThread().getName()+(char)( channel.write(buffer)));; //System.out.println(Thread.currentThread().getName()+(char)( channel.read(buffer)));; } catch (Exception e){ e.printStackTrace(); } } }