io和nio的缓冲_IO与NIO –中断,超时和缓冲区

io和nio的缓冲

假设有一个系统有时需要将文件复制到几个位置,但是这种方式在响应能力至关重要的情况下。 换句话说,如果由于某种原因文件系统过载,而我们无法在不到一秒钟的时间内写入文件,它应该放弃。

ExecutorService是一项非常方便的工作工具。 您可以轻松地将其用于并行执行多个任务(每个任务都写入不同的文件系统)。 Yuo还可以告诉它在超时后放弃,它将为您打断他们。 完美,正是我们所需要的。

脚手架看起来像这样:

void testCopy() throws Exception {ThreadPoolExecutor exec = (ThreadPoolExecutor) Executors.newCachedThreadPool();final long start = System.currentTimeMillis();Callable<Object> task = new Callable<Object>() {@Overridepublic Object call() throws Exception {try {copy("a.bin", "b.bin");} catch (Exception e) {e.printStackTrace();}System.out.println("Call really finished after: "+ (System.currentTimeMillis() - start));return null;}};Collection<Callable<Object>> taskWrapper = Arrays.asList(task);List<Future<Object>> futures = exec.invokeAll(taskWrapper, 50,TimeUnit.MILLISECONDS);System.out.println("invokeAll finished after: "+ (System.currentTimeMillis() - start));System.out.println("Future.isCancelled? "+ futures.get(0).isCancelled());Thread.sleep(20);System.out.println("Threads still active: " + exec.getActiveCount());
}

为了在低负载的运行状况良好的系统上模拟对超时的响应,我使用了100 MB的文件并且超时非常短。 任务总是超时,我的系统无法在50毫秒内复制100 MB。

我期望得到以下结果:

  1. 大约50毫秒后, invokeAll完成。
  2. Future.isCancelled? 是真的。
  3. 活动线程计数为0。通过睡眠可以消除某些边缘情况。 长话短说,它为复制功能提供了一些时间来检测中断。
  4. 通话大约在50毫秒后真正完成。 这非常重要,我绝对不希望取消任务后继续执行IO操作。 在较高的负载下,这会导致过多的线程卡在虚假的IO中。

以防万一,这些测试是在64位Windows 7上的Oracle 1.6 JVM上运行的。

解决方案1:流复制

第一次尝试可能很简单–使用带有缓冲区和经典IO的循环,如下所示:

private void copy(String in, String out) throws Exception {FileInputStream fin = new FileInputStream(in);FileOutputStream fout = new FileOutputStream(out);byte[] buf = new byte[4096];int read;while ((read = fin.read(buf)) > -1) {fout.write(buf, 0, read);}fin.close();fout.close();
}

这就是所有流行的流复制库做的,包括IOUtils Apache的共享和ByteStreams番石榴。

它也不幸地失败了:

invokeAll finished after: 53
Future.isCancelled? true
Threads still active: 1
Call really finished after: 338

原因很明显:在循环中或任何地方都不检查线程中断状态,因此线程可以正常继续。

解决方案2:通过复制检查流是否中断

让我们解决这个问题! 一种方法是:

while ((read = fin.read(buf)) > -1) {fout.write(buf, 0, read);if (Thread.interrupted()) {throw new IOException("Thread interrupted, cancelling");}
}

现在可以正常工作了,打印:

invokeAll finished after: 52
java.io.IOException: Thread interrupted, cancellingat TransferTest.copyInterruptingStream(TransferTest.java:75)at TransferTest.access$0(TransferTest.java:66)at TransferTest$1.call(TransferTest.java:25)at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)at java.util.concurrent.FutureTask.run(FutureTask.java:138)at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)Future.isCancelled? trueat java.lang.Thread.run(Thread.java:662)Call really finished after: 53
Threads still active: 0

很好,但是我觉得不满意。 它看起来很脏,我对自己的IO库中的这段代码并不特别满意。 必须有更好的方法,这将我们带到……

解决方案3:带传输的NIO

NIO具有这个不错的功能,它实际上尊重线程中断。 如果在线程中断后尝试读取或写入通道,则会收到ClosedByInterruptException

那正是我所需要的。 由于某种原因,我还在StackOverflow上阅读了以下答案 :

“如果不需要,请不要使用缓冲区。 如果目标是其他磁盘或NIC,为什么还要复制到内存? 对于较大的文件,确保的延迟是不平凡的。 (…)使用FileChannel.transferTo()FileChannel.transferFrom() 。 此处的主要优势在于,JVM使用操作系统对DMA(直接内存访问)的访问(如果存在)。 (这取决于实现,但是在通用CPU上使用现代的Sun和IBM版本是不错的选择。)发生的情况是,数据直接通过/从磁盘,到总线,再到目的地……直接通过RAM传递任何电路或CPU。”

太好了,让我们做吧!

private void copy(String in, String out) throws Exception {FileChannel fin = new FileInputStream(in).getChannel();FileChannel fout = new FileOutputStream(out).getChannel();fout.transferFrom(fin, 0, new File(in).length());fin.close();fout.close();
}

输出:

invokeAll finished after: 52
Future.isCancelled? true
Threads still active: 1
java.nio.channels.ClosedByInterruptExceptionat java.nio.channels.spi.AbstractInterruptibleChannel.end(AbstractInterruptibleChannel.java:184)at sun.nio.ch.FileChannelImpl.size(FileChannelImpl.java:304)at sun.nio.ch.FileChannelImpl.transferFrom(FileChannelImpl.java:587)at TransferTest.copyNioTransfer(TransferTest.java:91)at TransferTest.access$0(TransferTest.java:87)at TransferTest$1.call(TransferTest.java:27)at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)at java.util.concurrent.FutureTask.run(FutureTask.java:138)at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)at java.lang.Thread.run(Thread.java:662)
Call really finished after: 146

我要做的只是简单地调用transferFrom 。 非常简洁,并承诺会从硬件和操作系统中获得如此多的支持……但是,请稍等一下,为什么要花146毫秒? 我的意思是,146毫秒比第一次测试中的338毫秒快得多,但是我希望它在50毫秒后终止。

让我们在大约1.5 GB的更大文件上重复测试:

invokeAll finished after: 9012
Future.isCancelled? true
Threads still active: 1
java.nio.channels.ClosedByInterruptExceptionat java.nio.channels.spi.AbstractInterruptibleChannel.end(AbstractInterruptibleChannel.java:184)(...)
Call really finished after: 9170

那有多可怕? 这可能是可能发生的最糟糕的事情:

  • 任务未及时中断。 9秒太长了,我预计约为50毫秒。
  • 在整个操作过程中(9秒), invokeAll被阻止。 我勒个去?

解决方案4 –带缓冲的NIO

事实证明,我确实需要一些缓冲。 让我们尝试一下:

private void copyNioBuffered(String in, String out) throws Exception {FileChannel fin = new FileInputStream(in).getChannel();FileChannel fout = new FileOutputStream(out).getChannel();ByteBuffer buff = ByteBuffer.allocate(4096);while (fin.read(buff) != -1 || buff.position() > 0) {buff.flip();fout.write(buff);buff.compact();}fin.close();fout.close();
}

输出:

invokeAll finished after: 52
Future.isCancelled? true
java.nio.channels.ClosedByInterruptExceptionat java.nio.channels.spi.AbstractInterruptibleChannel.end(AbstractInterruptibleChannel.java:184)at sun.nio.ch.FileChannelImpl.write(FileChannelImpl.java:203)at TransferTest.copyNioBuffered(TransferTest.java:105)at TransferTest.access$0(TransferTest.java:98)at TransferTest$1.call(TransferTest.java:29)at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)at java.util.concurrent.FutureTask.run(FutureTask.java:138)at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)at java.lang.Thread.run(Thread.java:662)
Call really finished after: 55
Threads still active: 0

现在正是我所需要的。 它本身就考虑到中断,因此我不需要整个IO实用程序进行那些繁琐的检查。 怪癖:不同类型的渠道

如果我的IO实用程序仅用于复制按名称获取的文件,如下所示:

static public void copy(String source, String destination)

…然后很容易为NIO重写方法。

但是,如果它是在流上运行的更通用的签名,该怎么办?

static public void copy(InputStream source, OutputStream destination)

NIO有一个Channels实用程序,它具有非常有用的方法,例如:

public static ReadableByteChannel newChannel(InputStream in)
public static WritableByteChannel newChannel(OutputStream out)

因此,似乎我们可以使用此帮助程序包装流并从可中断的NIO API中受益。 在我们查看源代码之前:

public static WritableByteChannel newChannel(final OutputStream out) {if (out == null) {throw new NullPointerException();}if (out instanceof FileOutputStream &&FileOutputStream.class.equals(out.getClass())) {return ((FileOutputStream)out).getChannel();}return new WritableByteChannelImpl(out);
}private static class WritableByteChannelImplextends AbstractInterruptibleChannel // Not really interruptibleimplements WritableByteChannel
{
// ... Ignores interrupts completely

小心! 如果您的流是文件流,它们将是可中断的。 否则,您很不走运–它只是一个愚蠢的包装器,更像是API兼容性的适配器。 假设杀死,总是检查源头。

参考: IO与NIO – 松鼠博客上来自我们JCG合作伙伴 Konrad Garus的中断,超时和缓冲区 。


翻译自: https://www.javacodegeeks.com/2012/07/io-vs-nio-interruptions-timeouts-and.html

io和nio的缓冲

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/353689.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

直接在apk中添加资源的研究

原文 http://blog.votzone.com/2018/05/12/apk-merge.html 之前接手过一个sdk的开发工作&#xff0c;在开发过程中有一个很重要的点就是尽量使用代码来创建控件&#xff0c;资源文件最好放到assets目录下&#xff0c;如果必须使用res资源&#xff0c;需要通过 getResources().g…

强制类型转换简介

强制类型转换简介 强制类型转换当操作数的类型不同&#xff0c;而且不属于基本数据类型时&#xff0c;经常需要强制类型转换&#xff0c;将操作数转化为所需要的类型。强制类型转换具有两种形式&#xff0c;称为显式强制转换和隐式强制类型转换。强制类型转换不改变原来数据的类…

JavaFX实际应用程序:SkedPal

“真实世界的应用程序”系列中的一个新条目。 这次是SkedPal &#xff0c;这是一个用于智能管理忙人生活的应用程序。 我一直在咨询SkedPal团队有关JavaFX的事宜&#xff0c;并且在他们决定开始使用我的CalendarFX框架来满足他们的日历要求时&#xff0c;我也在咨询他们。 在下…

url 特殊字符 传递参数解决方法

url 特殊字符 传递参数解决方法 有些符号在URL中是不能直接传递的&#xff0c;如果要在URL中传递这些特殊符号&#xff0c;那么就要使用他们的编码了。下表中列出了一些URL特殊符号及编码。十六进制值 1. URL 中号表示空格 %2B 2. 空格 URL中的空格可以用号或者编码 %20 3.…

chromium之histogram.h

histogram不知道是干啥的 // Histogram is an object that aggregates statistics, and can summarize them in // various forms, including ASCII graphical, HTML, and numerically (as a // vector of numbers corresponding to each of the aggregating buckets). google翻…

Java中的功能性FizzBu​​zz Kata

不久前&#xff0c;我使用Java 8流和lambda解决了FizzBu​​zz kata问题。 尽管最终结果是可行的&#xff0c;但中间步骤却没有。 我当然可以做得更好。 与往常一样&#xff0c;让我们​​从失败的测试开始&#xff1a; package remonsinnema.blog.fizzbuzz;import static or…

axis2 wsdl2java 使用方式

axis2 wsdl2java 使用方式(2011-04-15 22:41:43) 说明见&#xff1a;http://hi.baidu.com/aotori/blog/item/ee98efcdc6cc300301e92814.html 用wsdl2java简化客户端的编写 也许有很多读者会说“有没有搞错啊&#xff0c;只调用两个WebService方法用要写这么多代码&#xff0c;…

电脑播放视频的时候有杂音

一开始我还以为是视频本身自带的杂音&#xff0c;但是换了一个其它视频播放测试了一下&#xff0c;发现还是一样的结果。不甘心&#xff0c;又播放了一个音频&#xff0c;结果还是有杂音。 于是想是不是无意中把驱动卸载了&#xff0c;于是下载了驱动精灵补驱动。下载安装之后驱…

Axis2错误

Axis2错误 www.MyException.Cn 发布于&#xff1a;2012-09-18 16:21:42 浏览&#xff1a;70次 Axis2异常异常&#xff1a;java.lang.NoClassDefFoundError: org/apache/neethi/PolicyComponent 缺少&#xff1a;neethi-2.0.4.jar 异常&#xff1a;java.lang.ClassNotFound…

viewobject_只读ViewObject和声明性SQL模式

viewobject介绍 声明式SQL模式被认为是基于实体的视图对象的最有价值的优点之一。 在这种模式下&#xff0c;根据UI中显示的属性在运行时生成VOSQL。 例如&#xff0c;如果某个页面包含一个只有两列EmployeeId和FirstName的表&#xff0c;则查询将生成为“从Employees中选择Emp…

vue数组中对象属性变化页面不渲染问题

问题引入 Vue之所以能够监听Model状态的变化&#xff0c;是因为JavaScript语言本身提供了Proxy或者Object.observe()机制来监听对象状态的变化。但是&#xff0c;对于数组元素的赋值&#xff0c;却没有办法直接监听。 因此&#xff0c;如果我们直接对数组元素赋值 <ul>&l…

webservice生成客户端的方法

2011-11-09 20:33 webservice生成客户端的方法 目前为止webservice生成客户端方法比较多&#xff0c;我本身使用的主要有三种方式&#xff1a; &#xff08;1&#xff09;使用eclipse自带。 file->new->other->web services->web service client PS:这种方式生成的…

使用Hibernate(JPA)一键式删除

在旧版本的Hibernate中&#xff0c;我可以看到手册中指示的一键式删除 。 但是较新的版本不再包含此部分。 我不知道为什么。 因此&#xff0c;在这篇文章中&#xff0c;我来看看它是否仍然有效。 一键式删除部分显示&#xff1a; 有时一个接一个地删除收集元素可能效率极低。…

UDP和TCP

TCP (Transmission Control Protocol)和UDP(User Datagram Protocol)协议属于传输层协议。其中TCP提供IP环境下的数据可靠传输&#xff0c;它提供的服务包括数据流传送、可靠性、有效流控、全双工操作和多路复用。通过面向连接、端到端和可靠的数据包发送。通俗说&#xff0c;它…

MyEclipse6.0 安装axis2插件, 调用加密的SAP webservice

MyEclipse6.0 安装axis2插件, 调用加密的SAP webservice 6人收藏此文章, 我要收藏 发表于1个月前(2013-06-06 09:41) , 已有116次阅读 &#xff0c;共0个评论 首先鄙视一下自己&#xff0c;还在用myeclipse,竟然还是6.0版本&#xff0c;没办法&#xff0c;用习惯了&#xff0c…

Eclipse中要导出jar包中引用了第三方jar包怎么办

Eclipse中要导出jar包中引用了第三方jar包怎么办 (2009-07-20 15:28:44) 转载▼标签&#xff1a; it 分类&#xff1a; Eclipse 今天做个小的java程序&#xff0c;想要先将其导出成一个可执行的jar包&#xff01;向往常一样&#xff0c;单击菜单栏中的 File -> export,弹出…

枚举类型定义

enum orientation:byte { north 1, south 2, east 3, west4 } 注意&#xff1a;声明在代码的主体之外 转载于:https://www.cnblogs.com/judes/p/9042426.html

拖动滑块拼图背景图没显示_计划B? 那是计划N…没什么。 拼图于2015年问世

拖动滑块拼图背景图没显示真是一天 当典型的欧洲人逐渐破产时&#xff0c;美国的人们开始喝咖啡。 这就是为什么我在Mark Reinhold最近的新闻中睡个好觉的原因。 他在题为“ Project Jigsaw&#xff1a;火车晚点 ”的帖子中建议将Project Jigsaw推迟到下一个版本Java 9。 在最近…

vi 常用命令行

vi 常用命令行 vi 常用命令行 1.vi 模式   a) 一般模式&#xff1a; vi 处理文件时&#xff0c;一进入该文件&#xff0c;就是一般模式了.   b) 编辑模式&#xff1a;在一般模式下可以进行删除&#xff0c;复制&#xff0c;粘贴等操作&#xff0c;却无法进行编辑操作。等…

java keytool证书工具使用小结

Keytool 是一个Java数据证书的管理工具 ,Keytool将密钥&#xff08;key&#xff09;和证书&#xff08;certificates&#xff09;存在一个称为keystore的文件中在keystore里&#xff0c;包含两种数据:密钥实体&#xff08;Key entity&#xff09;-密钥&#xff08;secret key&a…