泰安肥城做网站的公司,装饰行业做网站,title 镇江网站建设,网站建设详细方案模板关于新JDK 7功能的另一篇博客文章。 这次我正在写有关新的AnsynchronousFileChannel类的文章。 我将在两周内深入分析新的JDK 7功能#xff0c;并决定连续编号我的帖子。 只是为了确保我不会感到困惑#xff1a;-)这是我关于Java 7的第七篇文章#xff08;我承认–碰巧–这也… 关于新JDK 7功能的另一篇博客文章。 这次我正在写有关新的AnsynchronousFileChannel类的文章。 我将在两周内深入分析新的JDK 7功能并决定连续编号我的帖子。 只是为了确保我不会感到困惑-)这是我关于Java 7的第七篇文章我承认–碰巧–这也有些令人困惑。 有效使用NIO.2异步文件通道是一个广泛的话题。 这里有一些事情要考虑。 我决定将这些内容分为四个职位。 在第一部分中我将介绍当您使用异步文件通道时所涉及的概念。 由于这些文件通道是异步工作的因此与常规I / O相比它们的性能很有意思。 第二部分处理诸如内存和CPU消耗之类的问题并说明如何在高性能方案中安全地使用新的NIO.2通道。 您还需要了解如何在不丢失数据的情况下关闭异步通道这是第三部分。 最后在第四部分中我们将研究并发性。 注意我不会解释异步文件通道的完整API。 那里有足够的帖子在这方面做得很好。 我的帖子更深入地介绍了实用性和使用异步文件通道时可能遇到的问题。 好吧足够模糊的谈话让我们开始吧。 这是一个代码片段它打开一个异步通道第7行将字节序列写入文件的开头第9行并等待结果返回第10行。 最后在第14行中关闭通道。 public class CallGraph_Default_AsynchronousFileChannel {private static AsynchronousFileChannel fileChannel;public static void main(String[] args) throws InterruptedException, IOException, ExecutionException {try {fileChannel AsynchronousFileChannel.open(Paths.get(E:/temp/afile.out), StandardOpenOption.READ,StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.DELETE_ON_CLOSE);FutureInteger future fileChannel.write(ByteBuffer.wrap(Hello.getBytes()), 0L);future.get();} catch (Exception e) {e.printStackTrace();} finally {fileChannel.close();}}
} 异步文件通道调用的重要参与者 在继续研究代码之前让我们快速介绍一下异步文件通道星系中涉及的概念。 图1中的调用图显示了对AsynchronousFileChannel类的open方法的调用中的序列图。 FileSystemProvider封装所有操作系统详细信息。 为了逗大家我在编写本文时正在使用Windows XP客户端。 因此WindowsFileSystemProvider调用实际创建文件的WindowsChannelFactory并调用WindowsAsynchronousFileChannelImpl后者返回其自身的实例。 最重要的概念是Iocp即I / O完成端口。 它是用于执行多个同时异步输入/输出操作的API。 创建完成端口对象并将其与许多文件句柄关联。 当在对象上请求I / O服务时将通过排队到I / O完成端口的消息来指示完成。 不向其他请求I / O服务的进程通知I / O服务已完成而是检查I / O完成端口的消息队列以确定其I / O请求的状态。 I / O完成端口管理多个线程及其并发。 从图中可以看出Iocp是AsynchronousChannelGroup的子类型。 因此在JDK 7异步通道中异步通道组被实现为I / O完成端口。 它拥有负责执行所请求的异步I / O操作的ThreadPool。 ThreadPool实际上封装了ThreadPoolExecutor它执行Java 1.5以来的所有多线程异步任务执行管理。 对异步文件通道的写操作将导致对ThreadPoolExecutor.execute方法的调用。 一些基准 查看性能总是很有趣。 异步非阻塞I / O必须快速对吗 为了找到该问题的答案我进行了基准分析。 同样我使用亨氏微小的基准框架来做到这一点。 我的机器是2.90 GHz的Intel Core i5-2310 CPU具有四个内核64位。 在基准测试中我需要一个基准。 我的基线是对普通文件的简单常规同步写入操作。 这是代码段 public class Performance_Benchmark_ConventionalFileAccessExample_1 implements Runnable {private static FileOutputStream outputfile;private static byte[] content Hello.getBytes();public static void main(String[] args) throws InterruptedException, IOException {try {System.out.println(Test: Performance_Benchmark_ConventionalFileAccessExample_1.class.getSimpleName());outputfile new FileOutputStream(new File(E:/temp/afile.out), true);Average average new PerformanceHarness().calculatePerf(new PerformanceChecker(1000, new Performance_Benchmark_ConventionalFileAccessExample_1()), 5);System.out.println(Mean: DecimalFormat.getInstance().format(average.mean()));System.out.println(Std. Deviation: DecimalFormat.getInstance().format(average.stddev()));} catch (Exception e) {e.printStackTrace();} finally {new SystemInformation().printThreadInfo(true);outputfile.close();new File(E:/temp/afile.out).delete();}}Overridepublic void run() {try {outputfile.write(content); // append content} catch (IOException e) {e.printStackTrace();}}
} 正如您在第25行中看到的那样基准测试将对普通文件执行一次写入操作。 这些是结果 Test: Performance_Benchmark_ConventionalFileAccessExample_1
Warming up ...
EPSILON:20:TESTTIME:1000:ACTTIME:1014:LOOPS:365947
EPSILON:20:TESTTIME:1000:ACTTIME:1014:LOOPS:372298
Starting test intervall ...
EPSILON:20:TESTTIME:1000:ACTTIME:1000:LOOPS:364706
EPSILON:20:TESTTIME:1000:ACTTIME:1014:LOOPS:368309
EPSILON:20:TESTTIME:1000:ACTTIME:1014:LOOPS:370288
EPSILON:20:TESTTIME:1000:ACTTIME:1001:LOOPS:364908
EPSILON:20:TESTTIME:1000:ACTTIME:1014:LOOPS:370820
Mean: 367.806,2
Std. Deviation: 2.588,665
Total started thread count: 12
Peak thread count: 6
Deamon thread count: 4
Thread count: 5 以下代码段是另一个基准该基准也向异步文件通道发出写操作第25行 public class Performance_Benchmark_AsynchronousFileChannel_1 implements Runnable {private static AsynchronousFileChannel outputfile;private static int fileindex 0;public static void main(String[] args) throws InterruptedException, IOException {try {System.out.println(Test: Performance_Benchmark_AsynchronousFileChannel_1.class.getSimpleName());outputfile AsynchronousFileChannel.open(Paths.get(E:/temp/afile.out), StandardOpenOption.WRITE,StandardOpenOption.CREATE, StandardOpenOption.DELETE_ON_CLOSE);Average average new PerformanceHarness().calculatePerf(new PerformanceChecker(1000,new Performance_Benchmark_AsynchronousFileChannel_1()), 5);System.out.println(Mean: DecimalFormat.getInstance().format(average.mean()));System.out.println(Std. Deviation: DecimalFormat.getInstance().format(average.stddev()));} catch (Exception e) {e.printStackTrace();} finally {new SystemInformation().printThreadInfo(true);outputfile.close();}}Overridepublic void run() {outputfile.write(ByteBuffer.wrap(Hello.getBytes()), fileindex * 5);}
} 这是我的机器上上述基准测试的结果 Test: Performance_Benchmark_AsynchronousFileChannel_1
Warming up ...
EPSILON:20:TESTTIME:1000:ACTTIME:1015:LOOPS:42667
EPSILON:20:TESTTIME:1000:ACTTIME:1015:LOOPS:193351
Starting test intervall ...
EPSILON:20:TESTTIME:1000:ACTTIME:1015:LOOPS:191268
EPSILON:20:TESTTIME:1000:ACTTIME:1015:LOOPS:186916
EPSILON:20:TESTTIME:1000:ACTTIME:1014:LOOPS:189842
EPSILON:20:TESTTIME:1000:ACTTIME:1014:LOOPS:191103
EPSILON:20:TESTTIME:1000:ACTTIME:1015:LOOPS:192005
Mean: 190.226,8
Std. Deviation: 1.795,733
Total started thread count: 17
Peak thread count: 11
Deamon thread count: 9
Thread count: 10 由于上面的代码片段执行相同的操作因此可以肯定地说异步文件通道不一定比常规I / O更快。 我认为这是一个有趣的结果。 在单线程基准测试中很难将常规I / O和NIO.2相互比较。 引入NIO.2是为了在高度并发的场景中提供I / O技术。 因此询问更快的速度NIO或常规I / O并不是一个正确的问题。 合适的问题是什么是“更多并发” 但是就目前而言以上结果表明 当只有一个线程发出I / O操作时请考虑使用常规I / O。 现在就足够了。 我已经解释了基本概念还指出了常规I / O仍然存在。 在第二篇文章中我将介绍使用默认异步文件通道时可能遇到的一些问题。 我还将展示如何通过应用一些更可行的设置来避免这些问题。 应用自定义线程池 异步文件处理并不是高性能的绿卡。 在上一篇文章中我证明了常规I / O可以比异步通道更快。 应用NIO.2文件通道时还需要了解一些其他重要事实。 默认情况下在NIO.2文件通道中执行所有异步I / O任务的Iocp类由所谓的“缓存”线程池支持。 这是一个线程池可以根据需要创建新线程但是会在可用时重用以前构造的线程。 查看Iocp持有的ThreadPool类的代码。 public class ThreadPool {
...private static final ThreadFactory defaultThreadFactory new ThreadFactory() {Overridepublic Thread newThread(Runnable r) {Thread t new Thread(r);t.setDaemon(true);return t;}};
...static ThreadPool createDefault() {...ExecutorService executor new ThreadPoolExecutor(0, Integer.MAX_VALUE,Long.MAX_VALUE, TimeUnit.MILLISECONDS,new SynchronousQueueRunnable(),threadFactory);return new ThreadPool(executor, false, initialSize);}
...
} 默认通道组中的线程池被构造为ThreadPoolExecutor最大线程数为Integer.MAX_VALUE保持时间为Long.MAX_VALUE。 线程由线程工厂创建为守护程序线程。 如果所有线程都忙则使用同步移交队列来触发线程创建。 此配置存在多个问题 如果您在异步通道上突发执行写入操作则将创建数千个工作线程这可能会导致OutOfMemoryError无法创建新的本机线程。 当JVM退出时所有守护进程线程都将被放弃-最终不执行块也不会取消堆栈。 在我的其他博客中我解释了为什么无限制线程池会引起麻烦。 因此如果您使用异步文件通道则可以选择使用自定义线程池而不是默认线程池。 以下代码段显示了示例自定义设置。 ThreadPoolExecutor pool new
ThreadPoolExecutor(5, 5, 0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueueRunnable(2500));
pool.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
AsynchronousFileChannel outputfile AsynchronousFileChannel.open(Paths.get(FILE_NAME), new HashSetStandardopenoption
(Arrays.asList(StandardOpenOption.WRITE, StandardOpenOption.CREATE)), pool); AsynchronousFileChannel的Javadoc指出自定义执行程序应“至少[...]支持无限制的工作队列并且不应在execute方法的调用者线程上运行任务”。 这是一个冒险的说法只有在资源不成问题的情况下才是合理的这种情况很少发生。 对于异步文件通道请使用有限线程池。 您不会遇到线程太多的问题也无法用工作队列任务来充斥您的堆。 在上面的示例中您有五个线程执行异步I / O任务并且工作队列可容纳2500个任务。 如果超过了容量限制则拒绝执行处理程序将实现CallerRunsPolicy在该处客户端必须同步执行写任务。 因为工作负载被“推回”到客户端并同步执行所以这可能极大地降低系统性能。 但是它也可以使您免受结果无法预测的更严重的问题的困扰。 最佳做法是使用有界线程池并保持线程池大小可配置以便您可以在运行时进行调整。 同样要了解有关可靠的线程池设置的更多信息请参阅我的其他博客条目。 具有同步移交队列和未限制最大线程池大小的线程池可能会激进地创建新线程因此通过消耗PC寄存器和Java堆栈JVM的运行时内存可能会严重损害系统稳定性。 异步任务的“时间越长”经过的时间您越有可能遇到此问题。 具有无限制工作队列和固定线程池大小的线程池可以激进地创建新的任务和对象从而通过过多的垃圾回收活动消耗堆内存和CPU从而严重损害系统稳定性。 异步任务越大大小越长经过时间您越有可能遇到此问题。 这就是将自定义线程池应用于异步文件通道的全部内容。 我在本系列的下一篇博客中将介绍如何安全地关闭异步通道而不丢失数据。 参考测试平台上的Java 77NIO.2文件通道–第1部分–简介测试平台上的Java 78NIO.2文件通道–第2部分–应用来自我们JCG合作伙伴 Niklas的自定义线程池。 翻译自: https://www.javacodegeeks.com/2012/04/java-7-8-nio2-file-channels-on-test.html