`

多线程IO操作(fork-join版)

 
阅读更多
接着上篇中没写完的(http://my.oschina.net/bluesroot/blog/223453),上篇中讲到很多,为完成对一个目录的扫描的频繁的IO操作,我们从单线程到多线程,从CountDownLatch到BlockingQueue,中间不免各种Callable和Future和ExecutorService等等,虽然纷繁,中间有些不免麻烦,但是最终仍紧紧贴着我们的需求和多线程操作这一主题。

随着jdk的版本升级,并发包也随之扩充了不少,上面博文中的API来源于jdk1.6,1.7的推出和新的api的新增,我们有了更安全更方便更高效的方式去解决上面的问题。


1.5的时候,我们更多的使用synchronized,并辅助其他的api的去操作多线程,线程的理解不深刻,这样的做法很多时候是很危险的;1.6到了,官方推荐使用线程池,线程池成熟且方便的为大家省去了很多不必要的麻烦,如今到了1.7的时候了,我们发觉它愈发高效。


最常用的,我们当然能使用ExecutorService来管理并调度线程池中的线程来执行任务。然后问题很分明,设计该线程的时候,我们该如何设置线程的数量呢?多了怕浪费,少了又不高效,好歹有个线程计算公式(线程数=cpu核心数 /(1-阻塞系数),阻塞系数在0-1之间取值),虽然不是绝对正确,却也能有参考。在java7中,针对这一情况,专门加入了针对ExecutorService效率和性能的改进版的fork-join API。


ForkJoinPool类可以根据可用的处理器数量和任务需求,动态的对线程进行管理。(真心高端...)Fork-join使用了work-stealing策略,即本线程在完成自己的任务之后,在发现其他的线程还有活没做完,则主动帮助其他的线程一起干。该策略的使用,不但提升了API的性能,而且有助于提高线程的利用率。(妈妈再也不用担心,我开多少线程大小的线程池了~~)


在这个例子中,为了更好的调度任务,我们提供一些ForkJoinTask的实例来配合ForkJoinPool的函数的使用。我们用ForkJoinTask来创建(fork任务),然后再将主线程join到任务的完成点上。这样就行了。

ForkJoinTask有两个子类,RecursiveAction和RecursiveTask。前者的子类主要用来执行不需要返回值的任务(是不是跟Runnable的说法类似?),而后者的子类,主要用于执行需要返回值的任务。(对了,跟Future的说法类似的)

Fork-join API主要用于处理那些有些规模合理的任务,即如果每个任务所分担的开销都不大(也没有不确定的循环),则该API就可以达到合理的吞吐量。此外,Fork-join的API希望所有的任务都不要有副作用(即不要改变共享状态),并且没有同步或者锁操作,而本例正是如此,所以,这里使用它,能大大提高效率和代码简洁。它的场景有限,大家切不可以为,它能代替谁,它的出现是为了简化和解决某些情况,而并非取代某些老牌的并发API,ExecutorService表示毫无压力..........


废话太多了,我们结合上篇文章中的场景需求,看看如何使用jdk7的新api,优雅且高效的解决该问题,代码如下:

01 publicclassFileSize{
02
03 privatefinalstaticForkJoinPoolforkJoinPool=newForkJoinPool();
04
05 privatestaticclassFileSizeFinderextendsRecursiveTask<Long>{
06 finalFilefile;
07
08 publicFileSizeFinder(finalFiletheFile){
09 file=theFile;
10 }
11
12 @OverridepublicLongcompute(){
13 longsize=0;
14 if(file.isFile()){
15 size=file.length();
16 }else{
17 finalFile[]children=file.listFiles();
18 if(children!=null){
19 List<ForkJoinTask<Long>>tasks=
20 newArrayList<ForkJoinTask<Long>>();
21 for(finalFilechild:children){
22 if(child.isFile()){
23 size+=child.length();
24 }else{
25 tasks.add(newFileSizeFinder(child));
26 }
27 }
28
29 for(finalForkJoinTask<Long>task:invokeAll(tasks)){
30 size+=task.join();
31 }
32 }
33 }
34
35 returnsize;
36 }
37 }
38
39 publicstaticvoidmain(finalString[]args){
40 finallongstart=System.nanoTime();
41 finallongtotal=forkJoinPool.invoke(
42 newFileSizeFinder(newFile("D:/tools")));
43 finallongend=System.nanoTime();
44 System.out.println("TotalSize:"+total);
45 System.out.println("Timetaken:"+(end-start)/1.0e9);
46 }
47 }

上述tools文件夹,放了tomcat,maven,spring的包和maven的仓库,各种子目录很多,大小是543M,cpu i5,4g内存,运行结果为:

1 TotalSize:570332850
2 Timetaken:0.697063339


上述代码中,我们创建了ForkJoinPool实例的引用,用static修饰,即他在整个应用程序中共享。随后定义一个名为FileSizeFinder的静态内部类,它继承RecursiveTask类,并实现了compute(),该方法可以用来作为任务的执行引擎。invokeAll()函数将等待所有的子任务完成之后,才会去执行下一步循环累加操作。在任务被阻塞期间,其执行线程并非什么也不做,而是可以被调度去做其他的任务,最后每个任务都将compute函数结束时返回计算的结果。


本例中,我们递归的将扫描任务进行分解,直到不能拆分。但一般来说,拆分粒度太细会显著增加线程调度的开销,所以我们不建议将问题拆分的过小。

java.until.concurrent包中,定义了很多线程安全的集合类,这个集合类既保证了并发编程环境下的数据安全性,同时也可以被当做同步点来使用。线程安全很重要,但是我们不想为此牺牲太多的性能。后面我们继续探讨concurrent中与并发应用性能息息相关的一些数据结构。
分享到:
评论

相关推荐

    parallel-stream-fork-join-pool

    这就是为什么在并行流中执行 IO(更常见的是阻塞调用)非常糟糕的原因:被阻塞的线程无法被 JVM 中的所有并行流使用。 为此,您必须改用 CompletableFuture(或在某些情况下使用 ManagedBlocker)。 不过那是另一篇...

    python多线程DAY05.txt

    多进程/多线程并发 : 任何任务 3. 基于fork的多进程并发程序 每当有一个客户端连接就创建一个新的进程 4. ftp文件服务程序 *********************************************** 多线程并发 threading 的多...

    免费超全面的Java基础类型,容器,并发,IO流,面向对象,Web编程等代码总结

    多线程并发访问,同步控制 线程间通信,等待/通知机制 锁锁机制,API详解 Fork/Join 框架机制详解 Executor线程池框架简介 面向对象 泛型机制与反射原理 Proxy动态代理机制详解 从整体上观察对象 网络开发 Servlet...

    积分管理系统java源码-knowledge:这是我的知识,包括我所有已知的

    多线程编程、线程池、fork-join、并发编程 annotation 枚举 泛型 反射 字符串和String研究 集合内容,List、Map 文件io和网络io bio、nio和aio 类加载器 常用设计模式 模板模式 单例模式 & 多例模式 代理模式 策略...

    Linux多线程服务端编程:使用muduo C++网络库

    4.6多线程与IO . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . 98 4.7用RAII 包装文件描述符.. . . . . . . . . . . . . . . . . . . . . . . . . . 99 4.8RAII 与fork() . . . . . . . . . ....

    nuaa-os-lab:AANUAA 2018操作系统实践-上机翻译

    根目录下有三个大文件夹,分别为三种类型的译文:文件读写,多进程,多线程,每个大文件夹下有很多小文件夹,一个小文件夹对应一道译文,每个译文的文件夹下有一个与文件夹同名的二进制文件和一个编译脚本,你只需要...

    jdk7新特性jdk8新特性

    Java 7开始引入了一种新的Fork/Join线程池,它可以执行一种特殊的任务:把一个大任务拆成多个小任务并行执行。 我们举个例子:如果要计算一个超大数组的和,最简单的做法是用一个循环在一个线程内完成: ```ascii ...

    一个进程池的服务器程序

    if (write_pid() ) //避免同时有多个该程序在运行 return -1; if (pipe(fd1) ) { perror("pipe failed"); exit(-1); } if (s_pipe(fd2) ) { perror("pipe failed"); exit(-1); } int port = atoi(argv...

    qinyuanpei.github.io:Github页面和编码页面的个人博客

    本文档由脚本自动生成,最后更新时间:2021-04-19 22:42:03,最后部署版本:如果想订阅我的博客,请使用Github的Watch...CSDN 一键三连自动化2021-01-14 - 使用多线程为你的Python爬虫提速的N种姿势,你会几种?2021-

    java8集合源码分析-AboutJava:java相关知识(理论,代码)相关知识均是看书,博客等地方获取再由自己整理,如存在侵权,请告诉我

    多线程基础 synchronized volatile 线程间的通信 锁(重入锁,读写锁) 并发工具 增强的Future CompletableFuture 线程池技术 Java线程池Executors ForkJoin框架 原子操作类 JVM 虚拟机结构 class 文件格式 Class...

    Android代码-EasyShow3D

    目前在尝试使用多线程解析OBJ模型,但整体效果并不算很好,仍在开发过程中。 传送门 这里提供两个示例模型 Screenshots 模型解析示例 接入 1.在根目录下的build.gradle添加: allprojects { repositories { .....

    linux网络编程-宋敬彬-part1

    4.4.1 多线程编程实例 127 4.4.2 Linux下线程创建函数pthread_create() 129 4.4.3 线程的结束函数pthread_join()和pthread_exit() 129 4.4.4 线程的属性 130 4.4.5 线程间的互斥 132 4.4.6 线程中使用信号...

    java8源码-java-learning-copy:java-学习-复制

    构建,分模块学习,涉及反射,代理,多线程,IO,集合类等核心知识。 如果觉得不错,请先在这个仓库上点个 star 吧,这也是对我的肯定和鼓励,谢谢了。 不定时进行调整和补充,需要关注更新的请 watch、star、fork ...

    基于python3.7利用Motor来异步读写Mongodb提高效率(推荐)

    如果采用多线程、多进程的方案确实有效,但编写麻烦、消耗系统资源大(pymongo还不允许fork线程中共用连接)。这里主要瓶颈在于IO,使用单线程异步操作就会效果很好。 Motor是一个异步mongodb driver,支持异步读写...

    java8源码-sun:java相关知识

    构建,分模块学习,涉及反射,代理,多线程,IO,集合类等核心知识。 如果觉得不错,请先在这个仓库上点个 star 吧,这也是对我的肯定和鼓励,谢谢了。 不定时进行调整和补充,需要关注更新的请 watch、star、fork ...

    java8源码-java-learning:java-learning.git

    构建,分模块学习,涉及反射,代理,多线程,IO,集合类等核心知识。 如果觉得不错,请先在这个仓库上点个 star 吧,这也是对我的肯定和鼓励,谢谢了。 不定时进行调整和补充,需要关注更新的请 watch、star、fork ...

    java8源码-laowu-java-learning:java基础测试项目

    构建,分模块学习,涉及反射,代理,多线程,IO,集合类等核心知识。 如果觉得不错,请先在这个仓库上点个 star 吧,这也是对我的肯定和鼓励,谢谢了。 不定时进行调整和补充,需要关注更新的请 watch、star、fork ...

    java-core-learning-example:关于Java核心技术学习积累的例子,是初学者及核心技术巩固的最佳实践

    未完成模块:阿里Java手册,java8,注解,fork / join,加解密等。 我微信:bysocket01,加入纯技术交流群,成长技术 给教程的开源代码仓库点个Star吧 帮忙分享该系列文章链接给更多的朋友 如果您对Java基础核心类...

    java8源码-test:仅供测试

    构建,分模块学习,涉及反射,代理,多线程,IO,集合类等核心知识。 如果觉得不错,请先在这个仓库上点个 star 吧,这也是对我的肯定和鼓励,谢谢了。 不定时进行调整和补充,需要关注更新的请 watch、star、fork ...

    java8源码-java-learning:java学习

    构建,分模块学习,涉及反射,代理,多线程,IO,集合类等核心知识。 如果觉得不错,请先在这个仓库上点个 star 吧,这也是对我的肯定和鼓励,谢谢了。 不定时进行调整和补充,需要关注更新的请 watch、star、fork ...

Global site tag (gtag.js) - Google Analytics