接着上篇中没写完的(
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,优雅且高效的解决该问题,代码如下:
03 |
private final static ForkJoinPoolforkJoinPool= new ForkJoinPool();
|
05 |
private static class FileSizeFinder extends RecursiveTask<Long>{
|
08 |
public FileSizeFinder( final FiletheFile){
|
12 |
@Override public Longcompute(){
|
17 |
final File[]children=file.listFiles();
|
19 |
List<ForkJoinTask<Long>>tasks=
|
20 |
new ArrayList<ForkJoinTask<Long>>();
|
21 |
for ( final Filechild:children){
|
25 |
tasks.add( new FileSizeFinder(child));
|
29 |
for ( final ForkJoinTask<Long>task:invokeAll(tasks)){
|
39 |
public static void main( final String[]args){
|
40 |
final long start=System.nanoTime();
|
41 |
final long total=forkJoinPool.invoke(
|
42 |
new FileSizeFinder( new File( "D:/tools" )));
|
43 |
final long end=System.nanoTime();
|
44 |
System.out.println( "TotalSize:" +total);
|
45 |
System.out.println( "Timetaken:" +(end-start)/ 1 .0e9);
|
上述tools文件夹,放了tomcat,maven,spring的包和maven的仓库,各种子目录很多,大小是543M,cpu i5,4g内存,运行结果为:
上述代码中,我们创建了ForkJoinPool实例的引用,用static修饰,即他在整个应用程序中共享。随后定义一个名为FileSizeFinder的静态内部类,它继承RecursiveTask类,并实现了compute(),该方法可以用来作为任务的执行引擎。invokeAll()函数将等待所有的子任务完成之后,才会去执行下一步循环累加操作。在任务被阻塞期间,其执行线程并非什么也不做,而是可以被调度去做其他的任务,最后每个任务都将compute函数结束时返回计算的结果。
本例中,我们递归的将扫描任务进行分解,直到不能拆分。但一般来说,拆分粒度太细会显著增加线程调度的开销,所以我们不建议将问题拆分的过小。
java.until.concurrent包中,定义了很多线程安全的集合类,这个集合类既保证了并发编程环境下的数据安全性,同时也可以被当做同步点来使用。线程安全很重要,但是我们不想为此牺牲太多的性能。后面我们继续探讨concurrent中与并发应用性能息息相关的一些数据结构。
分享到:
相关推荐
本资源摘要信息涵盖了嵌入式系统学习中的多个方面,包括栈和队列、标准IO和文件IO、数组和链表、快速排序算法、后台程序执行、多线程和进程的比较、创建子进程中的写时拷贝技术等。 1. 栈和队列的区别 栈和队列都...
这就是为什么在并行流中执行 IO(更常见的是阻塞调用)非常糟糕的原因:被阻塞的线程无法被 JVM 中的所有并行流使用。 为此,您必须改用 CompletableFuture(或在某些情况下使用 ManagedBlocker)。 不过那是另一篇...
多线程的优势在于能够在一个进程中并行执行任务,提高CPU的利用率,特别是在IO密集型任务中,可以利用线程间切换等待IO操作完成,避免CPU空闲。然而,对于CPU密集型任务,多进程可能更具优势,因为每个进程都有自己...
Linux多线程服务端编程是指在Linux操作系统环境下,使用多线程技术来实现网络服务端应用程序的技术。本文将详细介绍如何使用现代C++语言和muduo网络库在Linux平台上编写高性能的多线程TCP网络服务器,以及与此相关的...
C语言提供线程库pthreads,通过pthread_create()创建线程,pthread_join()等待线程结束,pthread_mutex_系列函数处理线程同步,防止竞态条件。线程并发可以提升程序性能,但同时也需要处理好线程安全问题。 六、...
多进程/多线程并发 : 任何任务 3. 基于fork的多进程并发程序 每当有一个客户端连接就创建一个新的进程 4. ftp文件服务程序 *********************************************** 多线程并发 threading 的多...
T-IO自诞生以来,已经经历了多个版本的迭代,文档中提到了T-IO的早期历史和版本变迁,包括T-IO被码云评为最有价值的开源项目、在2017年成为最受欢迎的开源软件之一,并列在热门开源项目中Star数第三、Fork数第五的...
4.6多线程与IO . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . 98 4.7用RAII 包装文件描述符.. . . . . . . . . . . . . . . . . . . . . . . . . . 99 4.8RAII 与fork() . . . . . . . . . ....
6. **线程安全**:如果应用程序是多线程的,还需要考虑排序操作的线程安全性,避免数据竞争问题。 7. **性能优化**:在处理大量数据时,可能会考虑使用并发排序算法,如Fork/Join框架下的ParallelSort,或者使用...
- 进程与线程:阐述了进程和线程的概念,以及fork(), exec(), waitpid()等进程控制函数,以及pthread_create(), pthread_join()等线程管理函数。 - 进程调度:讲解了Linux的调度策略,如SCHED_RR、SCHED_FIFO等。 ...
在性能上,由于线程切换开销小,多线程处理往往比多进程快,但过多的线程可能导致资源竞争,反而降低效率。 接下来讨论静态库与动态库。静态库在编译时会被合并到目标程序中,形成一个单独的可执行文件,这使得程序...
这意味着虽然我们可以使用多线程来处理IO等待,但当涉及到CPU密集型操作时,多线程并不会带来性能上的提升。为此,可以使用多进程来绕开GIL限制,充分利用CPU的多核优势。Tornado支持创建多个子进程来分别处理请求,...
9. **并发编程**:Java并发库包含许多高级工具,如ExecutorService、Future、Callable和Fork/Join框架,以及并发集合类,如ConcurrentHashMap和CopyOnWriteArrayList,帮助开发者编写高效、安全的多线程代码。...
《Java 7并发编程实战手册》是一本深入探讨Java并发编程的权威著作,它涵盖了大量实用的技巧和最佳实践,旨在帮助开发者在多线程环境下编写高效、安全的代码。这本书的实例代码提供了丰富的示例,使读者能够直观地...
多线程并发访问,同步控制 线程间通信,等待/通知机制 锁锁机制,API详解 Fork/Join 框架机制详解 Executor线程池框架简介 面向对象 泛型机制与反射原理 Proxy动态代理机制详解 从整体上观察对象 网络开发 Servlet...
多线程编程、线程池、fork-join、并发编程 annotation 枚举 泛型 反射 字符串和String研究 集合内容,List、Map 文件io和网络io bio、nio和aio 类加载器 常用设计模式 模板模式 单例模式 & 多例模式 代理模式 策略...
本教程集合了Linux系统编程的核心知识,涵盖了多个关键领域,旨在帮助学习者深入理解Linux操作系统及其编程接口。通过这些PPT资料,你可以系统地学习和掌握以下主要内容: 1. **计算机系统概论** - 了解计算机硬件...
Java多线程TCP Socket服务器源码与libfastcgi fork(fcgi-master)详解 在IT领域,尤其是网络编程中,TCP Socket服务器是构建网络服务的基础,而Java作为一门跨平台的编程语言,常用于实现高性能的网络应用。本篇...
而多线程则适用于IO密集型任务,例如等待网络响应或读写文件,因为线程可以在等待IO操作时切换到其他线程,提高了CPU利用率。 总结来说,Python的多进程和多线程是实现并发执行、提升效率的关键工具,而分布式系统...