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.pswp.cn/news/372939.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/372939.shtml
英文地址,請注明出處:http://en.pswp.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;趕時間就直…

OSGI –模塊化您的應用程序

由于我是模塊化&#xff0c;低耦合&#xff0c;高凝聚力等的大力擁護者&#xff0c;所以…… 我相信這項技術是我們使用Java平臺創建應用程序的突破。 使用OSGi&#xff0c;創建高度可擴展的應用程序非常簡單&#xff0c;例如參見Eclipse IDE。 我的目的不是要深入展示該技術的…

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: …

三、自定義視圖、視圖控制器

1.自定義視圖 自定義視圖&#xff1a;系統標準UI之外&#xff0c;自己組合而出的新的視圖。在實際開發中&#xff0c;我們經常需要自己定義視圖&#xff0c;積累自己的代碼庫。自己封裝的視圖&#xff0c;能像系統提供的UI控件一樣用于多個項目中&#xff0c;這樣可以提高我們的…

程序如何在兩個gpu卡上并行運行_深度學習分布式訓練相關介紹 - Part 1 多GPU訓練...

本篇文章主要是對深度學習中運用多GPU進行訓練的一些基本的知識點進行的一個梳理文章中的內容都是經過認真地分析&#xff0c;并且盡量做到有所考證拋磚引玉&#xff0c;希望可以給大家有更多的啟發&#xff0c;并能有所收獲介紹大多數時候&#xff0c;梯度下降算法的訓練需要較…

集成Spring和JavaServer Faces:改進的模板

隨著2.0版的發布&#xff0c;Facelet模板成為JSF規范的核心部分。 使用<ui&#xff1a;composition>和<ui&#xff1a;decorate>標記&#xff0c;可以輕松構建復雜的頁面&#xff0c;同時仍保持標記清晰。 模板在創建HTML表單時特別有用&#xff0c;但是不幸的是&a…

whmcs模板路徑

whmcs網站根目錄 比如你的域名是server.nongbin.vip&#xff0c;你需要cd /home/wwwroot/server.nongbin.vip&#xff0c;該目錄下然后&#xff0c;cd template/ 給文件夾下就是你上傳的模板文件夾轉載于:https://www.cnblogs.com/nongbin/p/6412108.html

系統英偉達gpu驅動卸載_繞過CPU,英偉達讓GPU直連存儲設備

英偉達最近發布了一個新的GPUDirect Storage&#xff0c;暫且叫做GPU直連存儲&#xff0c;讓GPU直接連到NVMe存儲設備上。這一方案用到了RDMA設備來把數據從閃存存儲轉移到GPU本地的內存里&#xff0c;無需經過CPU還有系統內存。如果這一舉措順利的話&#xff0c;英偉達就能擺脫…

37、EnumSet詳解

EnumSet類也是有順序的&#xff0c;EnumSet按照枚舉值在Enum類內定義的順序決定集合元素的順序 EnumSet在內部已位向量的形式存儲&#xff0c;這種存儲方式非常緊湊、搞笑&#xff0c;因此EnumSet占用內存很小&#xff0c;而且運行效率很好。 EnumSet集合不允許加入null元素 En…

嘲弄和存根–了解Mockito的測試雙打

介紹 我遇到的一件事是使用模擬框架的團隊假設他們在模擬。 他們并不知道Mocks只是Gerard Meszaros在xunitpatterns.com上歸類的“測試雙打”之一。 重要的是要意識到每種類型的測試雙精度在測試中都扮演著不同的角色。 用與您需要學習不同模式或重構的方式相同&#xff0c;您…

numpy 辨異(三)—— hstack/column_stack,linalg.eig/linalg.eigh

1. np.hstack np.column_stack >>> np.hstack([np.array([1, 2, 3]), np.array([4, 5, 6])]) array([1, 2, 3, 4, 5, 6])>>> np.column_stack([np.array([1, 2, 3]), np.array([4, 5, 6])]) array([[1, 4],[2, 5],[3, 6]]) 當然對等地&#xff0c;也存在&…

【代碼筆記】iOS-首頁3張圖片變化

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

acwing算法提高之動態規劃--數位DP

目錄 1 基礎知識2 模板3 訓練 1 基礎知識 暫無。。。 2 模板 暫無。。。 3 訓練 題目1&#xff1a;度的數量。 解題思路&#xff1a;分類討論。 C代碼如下&#xff0c; #include <iostream> #include <vector>using namespace std;const int N 35; int K,…

python 輸入數字變成密碼_如何在python中檢查數字的“密碼”

我建議使用sets和stdlib中的string包作為可接受字符的列表。在我還建議進行一點重構&#xff0c;以刪除大量帶有if / else分支的嵌套。在import stringupper set(list(string.uppercase))lower set(list(string.lowercase))numbers set(list(string.digits))while True:npw …

使用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/下載&…

jquery的load方法

load方法指定一個界面會顯示在目標的標簽內部 比如MVC的一個分部視圖頁面想要顯示在某個標簽里面&#xff0c;可以寫成 $(標簽ID).load&#xff08;分部視圖名稱,data&#xff09; 其中第二個參數可選&#xff0c;主要是一些需要傳遞到該頁面的數據JSON格式組成&#xff0c;發送…

android 錄音原始文件_音頻采集:Android基于AudioRecord的實現

前言這篇文章簡單介紹下移動端Android系統下利用AudioRecord進行音頻采集方法。按照慣例開始前先提供一份源碼 AudioRecordLib 。AudioRecord采集的核心實現在于 AudioRecordCore.java 這個文件。權限申請想要使用AudioRecord這個API&#xff0c;需要在AndroidManifest.xml的配…

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…