线程池之ThreadPool与ForkJoinPool
网上对java线程池都有很多非常具体的解析,我概念性进行总结下,如有错误,可与我联系修改。
1.1 threadpool executor
一个线程池包括以下四个基本组成部分:
1、线程池管理器(threadpool):用于创建并管理线程池,包括 创建线程池,销毁线程池,添加新任务;
2、工作线程(poolworker):线程池中线程,在没有任务时处于等待状态,可以循环的执行任务;
3、任务接口(task):每个任务必须实现的接口,以供工作线程调度任务的执行,它主要规定了任务的入口,任务执行完后的收尾工作,任务的执行状态等;
4、任务队列(taskqueue):用于存放没有处理的任务。提供一种缓冲机制。
工作方式:
线程池有一个工作队列,队列中包含了要分配给各线程的工作。当线程空闲时,就会从队列中认领工作。由于线程资源的创建与销毁开销很大,所以threadpool允许线程的重用,减少创建与销毁的次数,提高效率。
流程图细节:
1.2 forkjoinpool executor
forkjoinpool组成类:
1,forkjoinpool:充当fork/join框架里面的管理者,最原始的任务都要交给它才能处理。它负责控制整个fork/join有多少个workerthread,workerthread的创建,激活都是由它来掌控。它还负责workqueue队列的创建和分配,每当创建一个workerthread,它负责分配相应的workqueue。然后它把接到的活都交给workerthread去处理,它可以说是整个frok/join的容器。
2,forkjoinworkerthread:fork/join里面真正干活的"工人",本质是一个线程。里面有一个forkjoinpool.workqueue的队列存放着它要干的活,接活之前它要向forkjoinpool注册(registerworker),拿到相应的workqueue。然后就从workqueue里面拿任务出来处理。它是依附于forkjoinpool而存活,如果forkjoinpool的销毁了,它也会跟着结束。
3,forkjoinpool.workqueue: 双端队列就是它,它负责存储接收的任务。
4,forkjointask:代表fork/join里面任务类型,我们一般用它的两个子类recursivetask、recursiveaction。这两个区别在于recursivetask任务是有返回值,recursiveaction没有返回值。任务的处理逻辑包括任务的切分都集中在compute()方法里面。
工作方式:
使用一种分治算法,递归地将任务分割成更小的子任务,其中阈值可配置,然后把子任务分配给不同的线程执行并发执行,最后再把结果组合起来。该用法常见于数组与集合的运算。
由于提交的任务不一定能够递归地分割成forkjointask,且forkjointask执行时间不等长,所以forkjoinpool使用一种工作窃取的算法,允许空闲的线程“窃取”分配给另一个线程的工作。由于工作无法平均分配并执行。所以工作窃取算法能更高效地利用硬件资源。
流程图细节:
1.3 应用场景
threadpool:多见于线程并发,阻塞时延比较长的,这种线程池比较常用,一般设置的线程个数根据业务性能要求会比较多。
forkjoinpool:特点是少量线程完成大量任务,一般用于非阻塞的,能快速处理的业务,或阻塞时延比较少的。