不学无数——JAVA中NIO再深入
JAVA中NIO再深入
在上一章节的JAVA中的I/O和NIO我们学习了如何使用NIO,接下来再深入了解一下关于NIO的知识。
缓冲器内部的细节
Buffer
由数据和可以高效地访问及操作这些数据的四个索引组成。这四个索引是
-
mark
:标记,就像游戏中设置了一个存档一样,可以调用reset()
方法进行回归到mark标记的地方。 -
position
:位置,其实缓冲器实际上就是一个美化过的数组,从通道中读取数据就是放到了底层的数组。所以其实就像索引一样。所以positon变量跟踪已经写了多少数据。 -
limit
:界限,即表明还有多少数据需要取出,或者还有多少空间能够写入。 -
capacity
:容量,表明缓冲器中可以存储的最大容量。
在缓冲器中每一个读写操作都会改变缓冲器的状态,用于反应所发生的变化。通过记录和跟踪这些变化,缓冲器就能够内部地管理自己的资源。下面是用于设置和复位索引以及查询其索引值的方法
方法名 | 解释 |
---|---|
capacity() | 返回缓冲器的容量 |
clear() | 清空缓冲器,将position设置为0,limit设置容量。调用此方法复写缓冲器 |
flip() | 将limit设置为position,position设置为0.此方法用于准备从缓冲区读取已经写入的数据 |
limit() | 返回limit值 |
limit(int lim) | 设置limit值 |
mark() | 将mark设置为positon |
position() | 返回position的值 |
position(int pos) | 设置postion的值 |
remaining() | 返回limit-position的值 |
接下来我们写个例子模拟这四个索引的变化情况,例如有一个字符串BuXueWuShu
。我们交换相邻的字符。
private static void symmetricScranble(CharBuffer buffer){ while (buffer.hasRemaining()){ buffer.mark(); char c1 = buffer.get(); char c2 = buffer.get(); buffer.reset(); buffer.put(c2).put(c1); } }
public static void main(String[] args) { char [] data = "BuXueWuShu".toCharArray(); ByteBuffer byteBuffer = ByteBuffer.allocate(data.length*2); CharBuffer charBuffer = byteBuffer.asCharBuffer(); charBuffer.put(data); System.out.println(charBuffer.rewind()); symmetricScranble(charBuffer); System.out.println(charBuffer.rewind()); symmetricScranble(charBuffer); System.out.println(charBuffer.rewind()); }
rewind()
方法是将position
设为0 ,mark
设为-1
在刚进入symmetricScranble ()
方法时的各个索引如下图所示
然后第一次调用了mark()
方法以后就相当于给mark
赋值了,相当于在此设置了一个回档点。此时索引如下所示
然后每次调用get()
方法Position
索引都会改变,在第一次调用了两次get()
方法以后,各个索引如下
然后调用了reset()
方法另Position=Mark
,此时的索引如下
然后每次调用put()
方法也会改变Position
索引的值,
注意此时前两个字符已经互换了位置。然后在第二轮while开始再次改变了Mark
索引的值,各个索引如下
此时我们应该就知道前面我们说的调用clear()
方法并不会清除缓冲器里面的数据的原因了,因为只是将其索引变了而已。
内存映射文件
内存映射文件不是Java引入的概念,而是操作系统提供的一种功能,大部分操作系统都支持。
内存映射文件允许我们创建和修改那些因为太大而不能放入内存的文件。有了内存映射文件,我们就可以假定整个文件都放在内存中,而且可以完全将其视为非常大的数组进行访问。所以对于文件的操作就变为了对于内存中的字节数组的操作,然后对于字节数组的操作会映射到文件中。这种映射可以映射整个文件,也可以只映射文件中的一部分。什么时候字节数组中的的操作会映射到文件上呢?这是由操作系统内部决定的。
内存放不下整个文件也不要紧,操作系统会自动进行处理,将需要的内容读到内存,将修改的内容保存到硬盘,将不再使用的内存释放。
如何用NIO将文件映射到内存中呢?下面有个小例子表示将文件的前1024个字节映射到内存中。
FileChannel fileChannel = new FileInputStream("").getChannel(); MappedByteBuffer map = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0, 1024);
创建一个内存映射文件只需要在通道中调用map()
方法即可,MapMode
有以下三个参数
-
READ_ONLY
:创建一个只读的映射文件 -
READ_WRITE
:创建一个既能读也能写的映射文件 -
PRIVATE
:创建一个写时拷贝(copy-on-write)的映射文件
我们可以简单的对比一下用内存映射文件对文件进行读写操作和用缓存Buffer
对文件进行读写操作的速度比较。
public static void main(String[] args) throws IOException { String fileName="/Users/hupengfei/Downloads/a.sql"; long t1=System.currentTimeMillis(); FileChannel fileChannel = new RandomAccessFile(fileName,"rw").getChannel(); IntBuffer map = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0, fileChannel.size()).asIntBuffer(); map.put(0); for (int i = 1; i < 50590; i++) { map.put(map.get(i-1)); } fileChannel.close(); long t=System.currentTimeMillis()-t1; System.out.println("Mapped Read/Write:"+t); long t2=System.currentTimeMillis(); RandomAccessFile randomAccessFile = new RandomAccessFile(new File(fileName),"rw"); randomAccessFile.writeInt(1); for (int i = 0 ; i<50590;i++){ randomAccessFile.seek(randomAccessFile.length()-4); randomAccessFile.writeInt(randomAccessFile.readInt()); } randomAccessFile.close(); long t22=System.currentTimeMillis()-t2; System.out.println("Stream Read/Write:"+t22); }
发现打印如下
Mapped Read/Write:29 Stream Read/Write:2439
文件越大,那么这个差异会更明显。
文件加锁
在JDK1.4中引入了文件加锁的机制,它允许我们同步的访问某个作为共享资源的文件。对于同一文件竞争的两个线程可能是来自于不同的操作系统,也可能是不同的进程,也可能是相同的进程,例如Java中两个线程对于文件的竞争。文件锁对于其他的操作系统的进程是可见的,因为文件加锁是直接映射到了本地操作系统的加锁工具。
下面举了一个简单的关于文件加锁的例子
public static void main(String[] args) throws IOException, InterruptedException { FileOutputStream fileOutputStream = new FileOutputStream("/Users/hupengfei/Downloads/a.sql"); FileLock fileLock = fileOutputStream.getChannel().tryLock(); if (fileLock != null){ System.out.println("Locked File"); TimeUnit.MICROSECONDS.sleep(100); fileLock.release(); System.out.println("Released Lock"); } fileLock.close(); }
通过对FileChannel
调用tryLock()
或者lock()
方法,就可以获得整个文件的FileLock
-
tryLock()
:是非阻塞的,如果不能获得锁,那么他就会直接从方法调用中返回 -
lock()
:是阻塞的,它要阻塞进程直到锁可以获得为止
调用FileLock.release()
可以释放锁。
当然也可以通过以下的方式对于文件的部分进行上锁
tryLock(long position,long size,boolean shared) lock(long position,long size,boolean shared)
对于加锁的区域是通过position
和size
进行限定的,而第三个参数指定是否为共享锁。无参数的加锁方法会对整个文件进行加锁,甚至文件变大以后也是如此。其中锁的类型是独占锁还是共享锁可以通过FileLock.isShared()
进行查询。
参考文章
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
如何通过 J2Cache 实现分布式 session 存储
做 Java Web 开发的人多数都会需要使用到 session (会话),我们使用 session 来保存一些需要在两个不同的请求之间共享数据。一般 Java 的 Web 容器像 Tomcat、Resin、Jetty 等等,它们会在内存中保存 session 数据。这样做会有两个不足: 1.服务重启后 session 数据丢失 2.应用做集群部署的时候,不同的节点无法共享 session 数据 我们以使用比例最高的 Tomcat 为例,针对第二个问题 Tomcat 提供了集群 session 复制的解决方案,详情请看官方文档。看完文档你会发现 Tomcat 自带的方法配置非常复杂,而且它没有解决第一个问题 —— 服务重启导致 session 数据丢失的问题。 现在还有另外一种方案就是使用 memcached 或者是 redis 来存储 session 数据,于是就有了这么一些开源项目: https://www.oschina.net/p/tomcat-redis-session-manager https://www.oschina.net/p/redis-manager https...
- 下一篇
Linux学习的回顾总结
Linux的学习也告一阶段了,其实学Linux也单纯是想着体验下使用双系统的感觉,想和在window系统一样日常的使用下Linux系统,所以才去学了些许知识,现在可以在Linux下用idea写Java,用网易云音乐听音乐,使用QQ微信等等基本功能,我的目的也达到了。今天就简单回顾看看之前自己写的几篇文章。 一、学习在VMware安装linux,熟悉些基本命令 二、尝试重装系统和安装双系统 三、福利篇——收集了在linux系统下解决的各种问题的文章链接 ~~~~~~~~~~~~~~~~~~~2018-09-27记录于此
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- SpringBoot2全家桶,快速入门学习开发网站教程
- CentOS8编译安装MySQL8.0.19
- SpringBoot2整合Redis,开启缓存,提高访问速度
- CentOS7,CentOS8安装Elasticsearch6.8.6
- CentOS6,CentOS7官方镜像安装Oracle11G
- Windows10,CentOS7,CentOS8安装Nodejs环境
- Red5直播服务器,属于Java语言的直播服务器
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- SpringBoot2整合Thymeleaf,官方推荐html解决方案
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7