原文连接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/java
同步非阻塞数组
阻塞与非阻塞的区别:缓存
Channel(通道)和Buffer(缓冲区)app
NIO以块的方式处理数据,可是IO是以最基础的字节流的形式去写入和读出的dom
NIO再也不是和IO同样用OutputStream和InputStream输入流的形式来进行处理数据的,可是又是基于这种流的方式,采用了通道和缓冲区的形式进行处理异步
NIO的通道是能够双向的,IO的流只能是单向的ide
NIO的缓冲区(字节数组)还能够进行分片,能够创建只读缓冲区、直接缓冲区和间接缓冲区,只读缓冲区就是只能够读,直接缓冲区是为了加快I/O速度,以一种特殊的方式分配其内存的缓冲区工具
NIO采用的是多路复用的IO模型,BIO用的是阻塞的IO模型性能
通道是对原I/O包中的流的模拟。到任何目的地的全部数据都必须经过一个Channel对象(通道)。一个Buffer实质上就是一个容器对象。发送给一个通道的全部对象都必须首先放到缓冲区中;从通道中读取的任何数据都要读到缓冲区中this
Buffer是一个对象,它包含一些要写入或者刚读出的数据。在NIO中加入Buffer对象,在流式IO中,将数据直接写入或者读到Stream对象中
在NIO库中,全部数据都是用缓冲区处理的。在读取数据时,它是直接读到缓冲区中的。在写入数据时,它是写入到缓冲区的。任什么时候候访问NIO中的数据,都须要将它放到缓冲区中
缓冲区实质上是一个数组。一般它是一个字节数组,可是也能够使用其余种类的数组。可是一个缓冲区不单单是一个数组,缓冲区提供了对数据的结构化访问,并且还能够跟踪系统的读/写进程
ByteBuffer CharBuffer ShortBuffer IntBuffer LongBuffer FloatBuffer DoubleBuffer
Selector是多路复用器,用于同时检测多个通道的事件以实现异步I/O。
经过一个选择器来同时对多个套接字通道进行监听,当套接字通道有可用的事件的时候,通道改成可用状态,选择器就能够实现可用的状态。
客户端-----》Channel-----》Selector------》keys--状态改变---》server
Buffer 缓冲区
Channel 通道
Selector 选择器
Server端建立ServerSocketChannel 有一个Selector多路复用器 轮询全部注册的通道,根据通道状态,执行相关操做
Client端建立SocketChannel 注册到Server端的Selector
有且仅有ByteBuffer(字节缓冲区)能够直接与通道交互。
public static void main(String[] args) { //生成FileChannel文件通道 FileChannel的操做--> 操做ByteBuffer用于读写,并独占式访问和锁定文件区域 // 写入文件 try(FileChannel fileChannel = new FileOutputStream(FILE).getChannel()){ fileChannel.write(ByteBuffer.wrap("test".getBytes())); } catch (IOException e){ throw new RuntimeException("写入文件失败",e); } // 在文件结尾写入 try(FileChannel fileChannel = new RandomAccessFile(FILE,"rw").getChannel()){ fileChannel.position(fileChannel.size());//移至文件结尾 fileChannel.write(ByteBuffer.wrap("some".getBytes())); } catch (IOException e){ throw new RuntimeException("写入文件结尾失败",e); } try(FileChannel fileChannel = new FileInputStream(FILE).getChannel(); FileChannel out = new FileOutputStream("C:\\Users\\sinosoft\\Desktop\\copy.txt").getChannel() ){ // 读取操做,须要调用allocate显示分配ByteBuffer ByteBuffer byteBuffer = ByteBuffer.allocate(1024); // read以后将数据放入缓冲区 while (fileChannel.read(byteBuffer) != -1){ byteBuffer.flip(); // 准备写入 out.write(byteBuffer); byteBuffer.clear(); // 清空缓存区 } } catch (IOException e){ throw new RuntimeException("读取文件失败",e); } }
rewind()方法是将position设置为缓冲区的开始位置
get()和put()都会修改position
get(int)和put(int)都不会修改position
mark()设置mark为当前position
flip()将写模式切换为读模式
public final Buffer flip() { limit = position; position = 0; mark = -1; return this; }
内存映射文件能够建立和修改那些由于太大而没法放入内存的文件。
RandomAccessFile tdat = new RandomAccessFile("test.dat", "rw"); MappedByteBuffer out = tdat.getChannel().map(FileChannel.MapMode.READ_WRITE, 0, length); 或者 FileChannel fc = new FileInputStream(new File("temp.tmp")).getChannel(); IntBuffer ib = fc.map(FileChannel.MapMode.READ_ONLY,0, fc.size()).asIntBuffer();
映射文件访问比标准IO性能高不少
文件锁定可同步访问,文件锁对其余操做系统进程可见,由于java文件锁直接映射到本机操做系统锁定工具。
public class FileLockTest { private static final String FILE = "C:\\Users\\sinosoft\\Desktop\\剩余工做副本.txt"; public static void main(String[] args) throws IOException, InterruptedException { FileChannel fileChannel = new FileOutputStream(FILE).getChannel(); // 文件锁 FileLock fileLock = fileChannel.tryLock(); Thread thread = new Thread(new Runnable() { @Override public void run() { FileChannel fileChannel = null; try { fileChannel = new FileOutputStream(FILE).getChannel(); } catch (FileNotFoundException e) { e.printStackTrace(); } ByteBuffer byteBuffer = ByteBuffer.allocate(1024); byteBuffer.put("aqws".getBytes()); try { System.out.println("线程准备写"); fileChannel.write(byteBuffer); System.out.println("线程写完"); } catch (IOException e) { e.printStackTrace(); } } }); thread.start(); if(fileLock != null){ ByteBuffer byteBuffer = ByteBuffer.allocate(1024); byteBuffer.put("aqwqdqdhwfwihfejfhi".getBytes()); System.out.println("主线程睡眠"); Thread.sleep(10000); // 会报错 java.nio.channels.NonWritableChannelException // fileChannel.read(byteBuffer); System.out.println("主线程准备写"); fileChannel.write(byteBuffer); fileLock.release(); } } } 主线程睡眠 线程准备写 java.io.IOException: 另外一个程序已锁定文件的一部分,进程没法访问。 at sun.nio.ch.FileDispatcherImpl.write0(Native Method) at sun.nio.ch.FileDispatcherImpl.write(FileDispatcherImpl.java:75) at sun.nio.ch.IOUtil.writeFromNativeBuffer(IOUtil.java:93) at sun.nio.ch.IOUtil.write(IOUtil.java:65) at sun.nio.ch.FileChannelImpl.write(FileChannelImpl.java:211) at com.zhanghe.study.io.nio.FileLockTest$1.run(FileLockTest.java:35) at java.lang.Thread.run(Thread.java:745) 主线程准备写
经过调用FileChannel上的tryLock或lock,能够得到整个文件的FileLock(SocketChannel、DatagramChannel和ServerSocketChannel不须要锁定,由于本质上就是单线程实体)
tryLock()是非阻塞的,试图获取锁,若不能获取,只是从方法调用返回
lock()会阻塞,直到得到锁,或者调用lock()的线程中断,或者调用lock()方法的通道关闭。
使用FileLock.release()释放锁
// 锁定文件的一部分,锁住size-position区域。第三个参数指定是否共享此锁 tryLock(long position, long size, boolean shared)
因为自己的博客百度没有收录,博客地址http://zhhll.icu