Java 7#8:测试台上的NIO.2文件通道

关于新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);Future<Integer> 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 SynchronousQueue<Runnable>(),threadFactory);return new ThreadPool(executor, false, initialSize);}
...
}

默认通道组中的线程池被构造为ThreadPoolExecutor,最大线程数为Integer.MAX_VALUE,保持时间为Long.MAX_VALUE。 线程由线程工厂创建为守护程序线程。 如果所有线程都忙,则使用同步移交队列来触发线程创建。 此配置存在多个问题:

  1. 如果您在异步通道上突发执行写入操作,则将创建数千个工作线程,这可能会导致OutOfMemoryError:无法创建新的本机线程。
  2. 当JVM退出时,所有守护进程线程都将被放弃-最终不执行块,也不会取消堆栈。

在我的其他博客中,我解释了为什么无限制线程池会引起麻烦。 因此,如果您使用异步文件通道,则可以选择使用自定义线程池而不是默认线程池。 以下代码段显示了示例自定义设置。

ThreadPoolExecutor pool = new 
ThreadPoolExecutor(5, 5, 0L, TimeUnit.MILLISECONDS, 
new LinkedBlockingQueue<Runnable>(2500));
pool.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
AsynchronousFileChannel outputfile = AsynchronousFileChannel.open(Paths.get(FILE_NAME), new HashSet<Standardopenoption>
(Arrays.asList(StandardOpenOption.WRITE, StandardOpenOption.CREATE)), pool);

AsynchronousFileChannel的Javadoc指出,自定义执行程序应“至少[...]支持无限制的工作队列,并且不应在execute方法的调用者线程上运行任务”。 这是一个冒险的说法,只有在资源不成问题的情况下才是合理的,这种情况很少发生。 对于异步文件通道,请使用有限线程池。 您不会遇到线程太多的问题,也无法用工作队列任务来充斥您的堆。 在上面的示例中,您有五个线程执行异步I / O任务,并且工作队列可容纳2500个任务。 如果超过了容量限制,则拒绝执行处理程序将实现CallerRunsPolicy,在该处客户端必须同步执行写任务。 因为工作负载被“推回”到客户端并同步执行,所以这可能(极大地)降低系统性能。 但是,它也可以使您免受结果无法预测的更严重的问题的困扰。 最佳做法是使用有界线程池并保持线程池大小可配置,以便您可以在运行时进行调整。 同样,要了解有关可靠的线程池设置的更多信息,请参阅我的其他博客条目。

具有同步移交队列和未限制最大线程池大小的线程池可能会激进地创建新线程,因此,通过消耗(PC寄存器和Java堆栈)JVM的运行时内存,可能会严重损害系统稳定性。 异步任务的“时间越长”(经过的时间),您越有可能遇到此问题。

具有无限制工作队列和固定线程池大小的线程池可以激进地创建新的任务和对象,从而通过过多的垃圾回收活动消耗堆内存和CPU,从而严重损害系统稳定性。 异步任务越大(大小)越长(经过时间),您越有可能遇到此问题。

这就是将自定义线程池应用于异步文件通道的全部内容。 我在本系列的下一篇博客中将介绍如何安全地关闭异步通道而不丢失数据。

参考:测试平台上的Java 7#7:NIO.2文件通道–第1部分–简介,测试平台上的Java 7#8:NIO.2文件通道–第2部分–应用来自我们JCG合作伙伴 Niklas的自定义线程池。


翻译自: https://www.javacodegeeks.com/2012/04/java-7-8-nio2-file-channels-on-test.html

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

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

相关文章

5页面title样式修改_认识html:实现网站页面是这么简单的一回事

互联网时代人们通过上网浏览信息&#xff0c;打开浏览器上网看到丰富的图文、视频、音乐等多媒体信息&#xff0c;一系列信息反馈和视觉冲击之后&#xff0c;您有没有想过&#xff0c;互联网这么发达的时代&#xff0c;您觉得花一点点时间学会做个网站页面不真香&#xff1f;概…

iOS指南针

前言&#xff1a; 这个小项目使用到了CoreLocation框架里面的设备朝向功能&#xff0c;对CoreLocation感兴趣的可以翻一下之前的文章 在另一个博客站有朋友发现一个尴尬的问题&#xff08;图片的东西2个方向是不对的&#xff09;&#xff0c;原谅我的大意&#xff0c;赶时间就直…

jq的链式调用.end();

jq的链式调用.end(); 先上code <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title><style>li{list-style: none;width: 100px;height:20px;border:1px solid #ff0000;display: …

程序如何在两个gpu卡上并行运行_深度学习分布式训练相关介绍 - Part 1 多GPU训练...

本篇文章主要是对深度学习中运用多GPU进行训练的一些基本的知识点进行的一个梳理文章中的内容都是经过认真地分析&#xff0c;并且尽量做到有所考证抛砖引玉&#xff0c;希望可以给大家有更多的启发&#xff0c;并能有所收获介绍大多数时候&#xff0c;梯度下降算法的训练需要较…

系统英伟达gpu驱动卸载_绕过CPU,英伟达让GPU直连存储设备

英伟达最近发布了一个新的GPUDirect Storage&#xff0c;暂且叫做GPU直连存储&#xff0c;让GPU直接连到NVMe存储设备上。这一方案用到了RDMA设备来把数据从闪存存储转移到GPU本地的内存里&#xff0c;无需经过CPU还有系统内存。如果这一举措顺利的话&#xff0c;英伟达就能摆脱…

嘲弄和存根–了解Mockito的测试双打

介绍 我遇到的一件事是使用模拟框架的团队假设他们在模拟。 他们并不知道Mocks只是Gerard Meszaros在xunitpatterns.com上归类的“测试双打”之一。 重要的是要意识到每种类型的测试双精度在测试中都扮演着不同的角色。 用与您需要学习不同模式或重构的方式相同&#xff0c;您…

【代码笔记】iOS-首页3张图片变化

一&#xff0c;效果图。 二&#xff0c;工程图。 三&#xff0c;代码。 RootViewController.h #import <UIKit/UIKit.h>interface RootViewController : UIViewController {NSTimer *timer;UIImageView *imageView1;UIImageView *imageView2;UIImageView *imageView3;UIV…

使用Eclipse在Amazon Ec2中部署Java Web应用程序的完整指南

嗨&#xff0c;读者们&#xff0c; 今天&#xff0c;我将向您展示如何使用Eclipse IDE在Amazon EC2中部署简单的Java Web应用程序。 在我们开始之前&#xff0c;我们需要一些必需的东西&#xff0c; Eclipse Java EE IDE –您可以从http://www.eclipse.org/downloads/下载&…

Spring 3和Java EE 6 –不公平和不完整的比较

这篇小文章的初稿标题为“ Spring&#xff06;Java EE –比较苹果和橙子”。 在撰写本文时&#xff0c;我了解到可以比较Spring Framework和Java EE&#xff0c;但这始终是不公平且不完整的工作。 Java for Enterprise和Spring Framework的发展紧密地联系在一起。 两者相互依存…

xml配置文件推荐方式

1.XML帮助类 /// <summary>/// Xml帮助类/// </summary>public class XmlHelper{/// <summary>/// 保存xml/// </summary>/// <typeparam name"T"></typeparam>/// <param name"path"></param>/// <p…

AFNetWorking https SSL认证

一般来讲如果app用了web service , 我们需要防止数据嗅探来保证数据安全.通常的做法是用ssl来连接以防止数据抓包和嗅探 其实这么做的话还是不够的 。 我们还需要防止中间人攻击&#xff08;不明白的自己去百度&#xff09;。攻击者通过伪造的ssl证书使app连接到了伪装的假冒的…

查看环境列表_Xfce 4.14桌面环境正式发布,想要图形界面又想节省内存?就它了...

1. Xfce 4.14桌面环境正式发布&#xff0c;它有什么新特性&#xff1f;本文主要讲解Xfce 4.14桌面环境正式发布&#xff0c;它有什么新特性。Xfce已经开发了4年多&#xff0c;但是这个周末终于看到了期待已久的Xfce 4.14的发布。Xfce 4.14是这个轻量级桌面环境的最新稳定版本&a…

卷积神经网络语音识别_用于物体识别的3D卷积神经网络

本文提出了一种基于CNN的3D物体识别方法&#xff0c;能够从3D图像表示中识别3D物体&#xff0c;并在比较了不同的体素时的准确性。已有文献中&#xff0c;3D CNN使用3D点云数据集或者RGBD图像来构建3D CNNs&#xff0c;但是CNN也可以用于直接识别物体体积表示的体素。本文中&am…

#获得请求来源ip_以太网数据包TCP、IP、ICMP、UDP、ARP协议头结构详解

以太网首部目地MAC地址(8字节)源MAC地址(8字节)类型(2字节)1、IP头的结构版本(4位)头长度(4位)服务类型(8位)封包总长度(16位)封包标识(16位)标志(3位)片断偏移地址(13位)存活时间(8位)协议(8位)校验和(16位)来源IP地址(32位)目的IP地址(32位)选项(可选)填充(可选)数据(1)字节和…

c# ef报错_C# EF调用MySql出现“未将对象引用设置到对象的实例”错误解决方案

C# EF调用MySql出现“未将对象引用设置到对象的实例”错误解决方案---修改步骤---1.打开Nuget管理包&#xff0c;把Mysql.Data替换为6.10.0以下任意版本。这里选择的是6.8.82.修改完毕后&#xff0c;继续把Mysql.Data.Entity也修改为对应版本6.8.8。3.安装完成后可以看到App.Co…

ServletRequest startAsync()的有用性有限

前段时间我遇到了Servlet 3.0中AsyncContext.start&#xff08;…&#xff09;的目的是什么&#xff1f; 题。 引用上述方法的Javadoc &#xff1a; 使容器调度线程&#xff08;可能从托管线程池中&#xff09;运行指定的Runnable 。 提醒大家&#xff0c; AsyncContext是Servl…

mysql所支持的比较运算符_mysql比较运算符有哪些?Mysql比较运算符详解

比较运算符可用于比较数字和字符串。今天发一篇Mysql比较运算符详解&#xff0c;希望对初学者有所帮助&#xff0c;虽然现在流行NoSQL&#xff0c;但是MYSQL还是很有用的&#xff0c;数字作为浮点值进行比较&#xff0c;字符串以不区为例进行比较&#xff0c;运算符用于比较表达…

Http Invoker的Spring Remoting支持

Spring HTTP Invoker是Java到Java远程处理的重要解决方案。 该技术使用标准的Java序列化机制通过HTTP公开服务&#xff0c;并且可以被视为替代解决方案&#xff0c;而不是Hessian和Burlap中的自定义序列化。 而且&#xff0c;它仅由Spring提供&#xff0c;因此客户端和服务器应…

学习后缀自动机想法

小序&#xff1a;学习后缀自动机是要有耐心的&#xff0c;clj的论文自己看真心酸爽&#xff01;&#xff08;还是自己太弱&#xff0c;ls&#xff0c;oyzx好劲啊&#xff0c;狂膜不止&#xff09; 刚刚在写博客之前又看了篇论文&#xff0c;终于看懂了&#xff0c;好开心 正文&…

mysql 分组top_MySQL:如何查询出每个分组中的 top n 条记录?

问题描述需求&#xff1a;查询出每月 order_amount(订单金额) 排行前3的记录。例如对于2019-02&#xff0c;查询结果中就应该是这3条&#xff1a;解决方法MySQL 5.7 和 MySQL 8.0 有不同的处理方法。1. MySQL 5.7我们先写一个查询语句。根据 order_date 中的年、月&#xff0c;…