垂直类网站怎么做推广,南宁网站建设服务商,网站开发维护关键技术,宁波市奉化区建设局网站假设有一个系统有时需要将文件复制到几个位置#xff0c;但是这种方式在响应速度至关重要的情况下。 换句话说#xff0c;如果由于某种原因文件系统过载#xff0c;并且我们无法在不到一秒钟的时间内写入文件#xff0c;则应该放弃。 ExecutorService是一项非常方便的工作工… 假设有一个系统有时需要将文件复制到几个位置但是这种方式在响应速度至关重要的情况下。 换句话说如果由于某种原因文件系统过载并且我们无法在不到一秒钟的时间内写入文件则应该放弃。 ExecutorService是一项非常方便的工作工具。 您可以轻松地将其用于并行执行多个任务每个任务都写入不同的文件系统。 Yuo还可以告诉它在超时后放弃它将为您打断他们。 完美正是我们所需要的。 脚手架看起来像这样 void testCopy() throws Exception {ThreadPoolExecutor exec (ThreadPoolExecutor) Executors.newCachedThreadPool();final long start System.currentTimeMillis();CallableObject task new CallableObject() {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;}};CollectionCallableObject taskWrapper Arrays.asList(task);ListFutureObject 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。 我期望得到以下结果 大约50毫秒后 invokeAll完成。 Future.isCancelled? 是真的。 活动线程计数为0。通过睡眠可以消除某些边缘情况。 长话短说它给了复制功能一些时间来检测中断。 通话大约在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