首页 > 编程知识 正文

forkjoin实际场景,dropout正则化的理解

时间:2023-05-06 00:07:56 阅读:21080 作者:1035

另一方面,ForkJoin ForkJoin框架包括ForkJoinTask、ForkJoinWorkerThread、ForkJoinPool和几个ForkJoinTask的子类,核心是3358 www.ssww

文章重点解说:使用ForkJoinPool、ForkJoinTask

福瑞克联合酒店

ForkJoinPool是JDK7中引入的线程池,它将一个大任务划分为n个小任务(fork ),并将多个小任务的处理合并为一个结果,即以join这一观点为中心。 大事变小,小事变小。 图解如下。

分治

每个线程有任务后,如果任务足够小,则直接运行任务逻辑;如果不太小,则继续划分为两个子任务,用fork方法继续分解,直到能够计算为止,然后逐级运行,从划分后的最小级别开始运行任务,再执行任务逐步递归。

工作窍取充分利用多核cpu的优势,执行多个“小任务”,将一个大任务划分为多个“小任务”,将多个“小任务”放置在多个处理器内核上并行执行

分治原理

每个线程维护自己的工作队列。 这是两端队列,可以先进先出(从队列开头获取任务),也可以先进后出。 可以在队列的两端进行队列操作。

特点

线程在自己工作队列队列的末尾获取任务并执行(LIFO后先进先出),如果没有任务,则尝试随机窃取具有其他任务的工作线程的队列头部并获取任务执行(FIFO先进先出)。 这可以减少一定的竞争。 fork采用了LIFO (后进先出),这保证了队列的开头任务都是更大的任务,结尾是分解的子任务。 偷更大的任务有助于这次偷窃的性价比高。

工作窃取

ForkJoinPool不需要通过submit直接继承执行ForkJoinTask类型任务的ForkJoinTask类,而只能继承其子类。 两个常见的子类如下:

RecursiveAction :递归事件,无返回值RecursiveTask :递归任务,有返回值(如果需要返回结果,继承此类即可)

原理:

对于如何快速积累1-10亿之间的和,本文阐述了三种方法,并向读者充分展示了每种方法的利弊

常用方法

publicstaticvoidmain (string [ ] args ) (/共1-10亿Long sum=0L; long start=system.current time millis (; for(longI=1L; i=10_0000_0000; I ) { sum =i; } longend=system.current time millis (; system.out.println('sum='sum '执行毫秒: ' ((结束-开始) ); }运行结果:运行速度与自己的电脑配置有关,作者为8核CPU

累加 1-10亿之间的和

a .首先定义ForkDemo类并计算结果:

使用ForkJoin计算:继承1-10亿之间的和ForkJoinTask实现类: RecursiveTask返回结果的类型2.compute (在实现方法的3.compute方法中计算结果, 如果任务大于阈值,则递归分解任务最后返回计算结果的是*/publicclassforkjoindemoextendsrecursivetasklong { privatelongstart; //起始值1私有长结束; //结束值10 _ 0000 _ 0000保密长时间=10000 l; //阈值。 分解的任务数大于阈值时,继续分割为较小的任务publicforkjoindemo(longstart,Long end )。 ({ this.start=start; this.end=end; } @ override保护性long compute (() /

1.当拆解的任务大于临界点值,则继续拆解任务 if((end - start) > temp){ // a.将大任务拆解为2个小任务 long middle = (end + start) / 2; // b.任务1 比如:累加1-100的和 ,任务1累加:1-50的和 ForkJoinDemo task1 = new ForkJoinDemo(start, middle); task1.fork(); // 拆分任务,把任务压入线程队列 // c.任务2 比如:累加1-100的和 ,任务2累加:51-100的和 ForkJoinDemo task2 = new ForkJoinDemo(middle+1, end); task2.fork(); // d.返回子任务的结果和 return task1.join() + task2.join(); // 2.当拆解的任务小于临界点值,直接计算并返回最后结果 }else{ Long sum = 0L; for(Long i=start; i<=end; i++){ sum+=i; } return sum; } }}

  b. 编写测试类,调用计算方法:

public static void main(String[] args) throws Exception { long start = System.currentTimeMillis(); // 1. 初始化一个线程池 ForkJoinPool pool = new ForkJoinPool(); // 2. 将任务提交到池中 (开始值:1 结束值:10_0000_0000) ForkJoinTask<Long> result = pool.submit(new ForkJoinDemo(1L, 10_0000_0000L)); // 3. 获取结果(可能会阻塞) Long sum = result.get(); long end = System.currentTimeMillis(); System.out.println("sum="+sum+" 执行毫秒:"+(end-start));}

  执行结果:执行速度会比for循环要快2秒

  

3、终极计算——使用Stream流式计算

public static void main(String[] args){ long start = System.currentTimeMillis(); long sum = LongStream.rangeClosed(1L, 10_0000_0000L).reduce(0, (a, b) -> a + b); long end = System.currentTimeMillis(); System.out.println("sum="+sum+" 执行毫秒:"+(end-start));}

执行结果:速度得到了质的飞跃

 LongStream 是Java8推出的流式计算中的一种

rangeClosed(long startInclusive, final long endInclusive)返回的是一个有序的LongStream。包含开始节点和结束节点两个参数之间所有的参数,间隔为1,例如rangeClosed(1,3)返回的是:1,2,3

reduce 合并流的元素并产生单个值。long reduce(long identity, LongBinaryOperator op),identity表示默认值或初始值,函数式接口,取两个值并产生一个新值。(注: java Function 函数中的 BinaryOperator 接口用于执行 lambda 表达式并返回一个 T 类型的返回值)

注意:不清楚函数式接口和Stream流式计算的小伙伴,可去参考我的博文前2篇文章,有做详细的说明和使用,这里就不贴出链接了。

扩展:上述流式方式是属于顺序流,如果要想速度更快,可以加上parallel方法生成并行流:

执行结果:比顺序流快了一倍,0.3秒!

说明:并行流就是一个把内容分成多个数据块,并用不同的线程分别处理每个数据块的流。这样在使用流处理数据规模较大的集合对象时可以充分的利用多核CPU来提高处理效率。

针对数据量小的,使用并行流反而比顺序流要更慢,比如:1到1000的整数求和。将流并行化所带来的额外开销比逻辑代码开销还大,所以要根据场景选择!

总结

ForkJoin对于大量的任务,可以充分利用资源,减少竞争,并且通过窃取算法。实现任务的负载均衡。但是对于较少的任务。多队列以及线程频繁的窃取会导致性能的急剧下降。针对阻塞时间久的任务,也不推荐使用,阻塞会占用线程资源。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。