Fork/Join是什么
传统线程池ThreadPoolExecutor有两个明显的缺点:一是无法对大任务进行拆分,对于某个任务只能由单线程执行;二是工作线程从队列中获取任务时存在竞争情况。
这两个缺点都会影响任务的执行效率。为了解决传统线程池的缺陷,Java7中引入Fork/Join框架,并在Java8中得到广泛应用。
ForkJoinPool是ThreadPoolExecutor线程池的一种补充,是对计算密集型场景的加强。
Fork/Join框架的核心是ForkJoinPool类,它是对AbstractExecutorService类的扩展。
ForkJoinPool允许其他线程向它提交任务,并根据设定将这些任务拆分为粒度更细的子任务,这些子任务将由ForkJoinPool内部的工作线程来并行执行,并且工作线程之间可以窃取彼此之间的任务。
Fork/Join的适用场景
最适合计算密集型任务。
最好是非阻塞任务。
任务总数、单任务执行耗时以及并行数都会影响到Fork/Join的性能。
Fork/Join的内容
分治任务的线程池:ForkJoinPool
分治任务:ForkJoinTask
分治任务的线程池:ForkJoinPool
ForkJoinPool是用于执行ForkJoinTask任务的执行池,不再是传统执行池Worker+Queue的组合式,而是维护了一个队列数组WorkQueue(WorkQueue[]),这样在提交任务和线程任务的时候大幅度减少碰撞。
ForkJoinPool中的任务执行可以去窃取其他线程的任务。
ForkJoinPool的构造方法
ForkJoinPool中有四个核心参数,用于控制线程池的并行数、工作线程的创建、异常处理和模式指定等。各参数解释如下:
intparallelism:指定并行级别(parallelismlevel)。ForkJoinPool将根据这个设定,决定工作线程的数量。如果未设置的话,将使用Runtime.getRuntime().availableProcessors()来设置并行级别;
ForkJoinWorkerThreadFactoryfactory:ForkJoinPool在创建线程时,会通过factory来创建。注意,这里需要实现的是ForkJoinWorkerThreadFactory,而不是ThreadFactory。如果你不指定factory,那么将由默认的DefaultForkJoinWorkerThreadFactory负责线程的创建工作;
UncaughtExceptionHandlerhandler:指定异常处理器,当任务在运行中出错时,将由设定的处理器处理;
booleanasyncMode:设置队列的工作模式:asyncMode?FIFO_QUEUE
IFO_QUEUE。当asyncMode为true时,将使用先进先出队列,而为false时则使用后进先出的模式。
ForkJoinPool的提交任务方式
execute方法:在提交任务后,不会返回结果。ForkJoinPool不仅允许提交ForkJoinTask类型任务,还允许提交Runnable任务。执行Runnable类型任务时,将会转换为ForkJoinTask类型。由于任务是不可切分的,所以这类任务无法获得任务拆分这方面的效益,不过仍然可以获得任务窃取带来的好处和性能提升。
invoke方法:接受ForkJoinTask类型的任务,并在任务执行结束后,返回泛型结果。如果提交的任务是null,将抛出空指针异常。
submit方法:支持三种类型的任务提交:ForkJoinTask类型、Callable类型和Runnable类型。在提交任务后,将返回ForkJoinTask类型的结果。如果提交的任务是null,将抛出空指针异常,并且当任务不能按计划执行的话,将抛出任务拒绝异常。
分治任务:ForkJoinTask
ForkJoinTask是ForkJoinPool的核心之一,它是任务的实际载体,定义了任务执行时的具体逻辑和拆分逻辑。
ForkJoinTask继承了Future接口,所以也可以将其看作是轻量级的Future。
ForkJoinTask是一个抽象类,它的方法有很多,最核心的是fork()方法和join()方法,承载着主要的任务协调作用,一个用于任务提交,一个用于结果获取。
提交任务fork():用于向当前任务所运行的线程池中提交任务。如果当前线程是ForkJoinWorkerThread类型,将会放入该线程的工作队列,否则放入