在这种情况下,ForkJoinPool可以比ExecutorService更快吗?

时间:2013-03-27 18:47:12

标签: java multithreading java-7 fork-join

只要我在池中提供ForkJoinPool 一个额外的线程,它的执行速度与ExecutorService相同。以下是使用的三个类:Main,RunnableTask和ForkJoinTask。在16核盒上运行,程序每次输出: 执行人时间:5002 ForkJoin时间:5002

主类:

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ForkJoinPool;

public class Main {

    public static void main(String[] args) throws InterruptedException {
        runExecutor(80);
        runForkJoin(80);
    }

    public static void runForkJoin(int size) {
        ForkJoinPool fjp = new ForkJoinPool(17);
        long start = System.currentTimeMillis();
        fjp.invoke(new ForkJoinTask(size));
        System.out.println("ForkJoin Time: "
                + (System.currentTimeMillis() - start));
        fjp.shutdown();
    }

    public static void runExecutor(int size) throws InterruptedException {
        ExecutorService exec = Executors.newFixedThreadPool(16);
        CountDownLatch latch = new CountDownLatch(size);
        long start = System.currentTimeMillis();
        for (int i = 0; i < latch.getCount(); i++) {
            exec.submit(new RunnableTask(latch));
        }
        latch.await();
        System.out.println("Executor Time: "
                + (System.currentTimeMillis() - start));
        exec.shutdown();
    }
}

Runnable类:

import java.util.concurrent.CountDownLatch;

public class RunnableTask implements Runnable {
    private CountDownLatch latch;

    public RunnableTask(CountDownLatch latch) {
        this.latch = latch;
    }

    @Override
    public void run() {
        try {
            Thread.sleep(1000);
            latch.countDown();
        } catch (Exception e) {
        }
    }
}

RecursiveTask类:

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.RecursiveTask;

public class ForkJoinTask extends RecursiveTask {
    private List<RecursiveTask> tasks;
    private int size;

    public ForkJoinTask(int size) {
        super();
        this.tasks = new ArrayList<>();
        this.size = size;
    }

    @Override
    protected Object compute() {
        for (int i = 0; i < size; i++) {
            RecursiveTask task = new RecursiveTask() {
                @Override
                protected Object compute() {
                    try {
                        Thread.sleep(1000);
                    } catch (Exception e) {

                    }
                    return null;
                }
            };
            task.fork();
            tasks.add(task);
        }

        for (RecursiveTask task : tasks) {
            task.join();
        }
        return null;
    }
}

1 个答案:

答案 0 :(得分:3)

您的个人任务可以使 ForkJoinPoolExecutorService比现在更快地运行,而且任何人都不应该有实质性的优势在另一方面。

原因是如果单个计算任务是Thread.sleep(1000),则该任务不需要CPU资源。您可以增加线程数以匹配您的工作大小(80)并在超过1秒的时间内完成80秒的“工作”,因为线程不会真正竞争任何类型的资源。

至于ForkJoinPoolExecutorService之间的比较,差异与您的测试用例无关,因为您的工作量不会导致任何应该作为进一步计算的输入(MapReduce中的'reduce'步骤)。所以对你来说,它们都只是具有不同API的线程池。