经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » Java » 查看文章
NIO基础操作
来源:cnblogs  作者:霍家姑爷  时间:2021/1/11 22:45:55  对本文有异议

原文链接http://zhhll.icu/2020/05/18/java%E5%9F%BA%E7%A1%80/IO/NIO%E5%9F%BA%E6%9C%AC%E6%93%8D%E4%BD%9C/

NIO

同步非阻塞

阻塞与非阻塞的区别:

  • 阻塞时,在调用结果返回时,当前线程会被挂起,并在得到结果之后返回
  • 非阻塞时,如不能立即得到结果,该调用不会阻塞当前线程,调用者需要定时轮询查看处理状态

Channel(通道)和Buffer(缓冲区)

与普通IO的不同和关系

  • NIO以块的方式处理数据,但是IO是以最基础的字节流的形式去写入和读出的

  • NIO不再是和IO一样用OutputStream和InputStream输入流的形式来进行处理数据的,但是又是基于这种流的方式,采用了通道和缓冲区的形式进行处理

  • NIO的通道是可以双向的,IO的流只能是单向的

  • NIO的缓冲区(字节数组)还可以进行分片,可以建立只读缓冲区、直接缓冲区和间接缓冲区,只读缓冲区就是只可以读,直接缓冲区是为了加快I/O速度,以一种特殊的方式分配其内存的缓冲区

  • NIO采用的是多路复用的IO模型,BIO用的是阻塞的IO模型

通道的概念

通道是对原I/O包中的流的模拟。到任何目的地的所有数据都必须通过一个Channel对象(通道)。一个Buffer实质上就是一个容器对象。发送给一个通道的所有对象都必须首先放到缓冲区中;从通道中读取的任何数据都要读到缓冲区中

缓冲区的概念

  • Buffer是一个对象,它包含一些要写入或者刚读出的数据。在NIO中加入Buffer对象,在流式IO中,将数据直接写入或者读到Stream对象中

  • 在NIO库中,所有数据都是用缓冲区处理的。在读取数据时,它是直接读到缓冲区中的。在写入数据时,它是写入到缓冲区的。任何时候访问NIO中的数据,都需要将它放到缓冲区中

  • 缓冲区实质上是一个数组。通常它是一个字节数组,但是也可以使用其他种类的数组。但是一个缓冲区不仅仅是一个数组,缓冲区提供了对数据的结构化访问,而且还可以跟踪系统的读/写进程

  1. ByteBuffer
  2. CharBuffer
  3. ShortBuffer
  4. IntBuffer
  5. LongBuffer
  6. FloatBuffer
  7. DoubleBuffer

选择器

Selector是多路复用器,用于同时检测多个通道的事件以实现异步I/O。

通过一个选择器来同时对多个套接字通道进行监听,当套接字通道有可用的事件的时候,通道改为可用状态,选择器就可以实现可用的状态。

工作原理

客户端-----》Channel-----》Selector------》keys--状态改变---》server

Buffer 缓冲区
Channel 通道
Selector 选择器

Server端创建ServerSocketChannel 有一个Selector多路复用器 轮询所有注册的通道,根据通道状态,执行相关操作

  • Connect 连接状态
  • Accept 阻塞状态
  • Read 可读状态
  • Write 可写状态

Client端创建SocketChannel 注册到Server端的Selector

buffer

  • capacity 缓冲区数组的总长度
  • position 下一个要操作的数据元素的位置
  • limit 缓冲区数组中不可操作的下一个元素的位置,limit<=capacity
  • mark 用于记录当前position的前一个位置或者默认是0
  • clear/flip/rewind等都是操作limit和position的值来实现重复读写的

ByteBuffer

有且仅有ByteBuffer(字节缓冲区)可以直接与通道交互。

  1. public static void main(String[] args) {
  2. //生成FileChannel文件通道 FileChannel的操作--> 操作ByteBuffer用于读写,并独占式访问和锁定文件区域
  3. // 写入文件
  4. try(FileChannel fileChannel = new FileOutputStream(FILE).getChannel()){
  5. fileChannel.write(ByteBuffer.wrap("test".getBytes()));
  6. } catch (IOException e){
  7. throw new RuntimeException("写入文件失败",e);
  8. }
  9. // 在文件结尾写入
  10. try(FileChannel fileChannel = new RandomAccessFile(FILE,"rw").getChannel()){
  11. fileChannel.position(fileChannel.size());//移至文件结尾
  12. fileChannel.write(ByteBuffer.wrap("some".getBytes()));
  13. } catch (IOException e){
  14. throw new RuntimeException("写入文件结尾失败",e);
  15. }
  16. try(FileChannel fileChannel = new FileInputStream(FILE).getChannel();
  17. FileChannel out = new FileOutputStream("C:\\Users\\sinosoft\\Desktop\\copy.txt").getChannel()
  18. ){
  19. // 读取操作,需要调用allocate显示分配ByteBuffer
  20. ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
  21. // read之后将数据放入缓冲区
  22. while (fileChannel.read(byteBuffer) != -1){
  23. byteBuffer.flip(); // 准备写入
  24. out.write(byteBuffer);
  25. byteBuffer.clear(); // 清空缓存区
  26. }
  27. } catch (IOException e){
  28. throw new RuntimeException("读取文件失败",e);
  29. }
  30. }

方法说明

rewind()方法是将position设置为缓冲区的开始位置

get()和put()都会修改position

get(int)和put(int)都不会修改position

mark()设置mark为当前position

flip()将写模式切换为读模式

  1. public final Buffer flip() {
  2. limit = position;
  3. position = 0;
  4. mark = -1;
  5. return this;
  6. }

内存映射文件

内存映射文件可以创建和修改那些因为太大而无法放入内存的文件。

  1. RandomAccessFile tdat = new RandomAccessFile("test.dat", "rw");
  2. MappedByteBuffer out = tdat.getChannel().map(FileChannel.MapMode.READ_WRITE, 0, length);
  3. 或者
  4. FileChannel fc = new FileInputStream(new File("temp.tmp")).getChannel();
  5. IntBuffer ib = fc.map(FileChannel.MapMode.READ_ONLY,0, fc.size()).asIntBuffer();

映射文件访问比标准IO性能高很多

文件锁定

文件锁定可同步访问,文件锁对其他操作系统进程可见,因为java文件锁直接映射到本机操作系统锁定工具。

  1. public class FileLockTest {
  2. private static final String FILE = "C:\\Users\\sinosoft\\Desktop\\剩余工作副本.txt";
  3. public static void main(String[] args) throws IOException, InterruptedException {
  4. FileChannel fileChannel = new FileOutputStream(FILE).getChannel();
  5. // 文件锁
  6. FileLock fileLock = fileChannel.tryLock();
  7. Thread thread = new Thread(new Runnable() {
  8. @Override
  9. public void run() {
  10. FileChannel fileChannel = null;
  11. try {
  12. fileChannel = new FileOutputStream(FILE).getChannel();
  13. } catch (FileNotFoundException e) {
  14. e.printStackTrace();
  15. }
  16. ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
  17. byteBuffer.put("aqws".getBytes());
  18. try {
  19. System.out.println("线程准备写");
  20. fileChannel.write(byteBuffer);
  21. System.out.println("线程写完");
  22. } catch (IOException e) {
  23. e.printStackTrace();
  24. }
  25. }
  26. });
  27. thread.start();
  28. if(fileLock != null){
  29. ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
  30. byteBuffer.put("aqwqdqdhwfwihfejfhi".getBytes());
  31. System.out.println("主线程睡眠");
  32. Thread.sleep(10000);
  33. // 会报错 java.nio.channels.NonWritableChannelException
  34. // fileChannel.read(byteBuffer);
  35. System.out.println("主线程准备写");
  36. fileChannel.write(byteBuffer);
  37. fileLock.release();
  38. }
  39. }
  40. }
  41. 主线程睡眠
  42. 线程准备写
  43. java.io.IOException: 另一个程序已锁定文件的一部分,进程无法访问。
  44. at sun.nio.ch.FileDispatcherImpl.write0(Native Method)
  45. at sun.nio.ch.FileDispatcherImpl.write(FileDispatcherImpl.java:75)
  46. at sun.nio.ch.IOUtil.writeFromNativeBuffer(IOUtil.java:93)
  47. at sun.nio.ch.IOUtil.write(IOUtil.java:65)
  48. at sun.nio.ch.FileChannelImpl.write(FileChannelImpl.java:211)
  49. at com.zhanghe.study.io.nio.FileLockTest$1.run(FileLockTest.java:35)
  50. at java.lang.Thread.run(Thread.java:745)
  51. 主线程准备写

通过调用FileChannel上的tryLock或lock,可以获得整个文件的FileLock(SocketChannel、DatagramChannel和ServerSocketChannel不需要锁定,因为本质上就是单线程实体)

tryLock()是非阻塞的,试图获取锁,若不能获取,只是从方法调用返回

lock()会阻塞,直到获得锁,或者调用lock()的线程中断,或者调用lock()方法的通道关闭。

使用FileLock.release()释放锁

  1. // 锁定文件的一部分,锁住size-position区域。第三个参数指定是否共享此锁
  2. tryLock(long position, long size, boolean shared)

由于本身的博客百度没有收录,博客地址http://zhhll.icu

原文链接:http://www.cnblogs.com/life-time/p/14264646.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号