固定线程池和大量任务的线程问题

时间:2014-10-17 17:45:09

标签: java multithreading performance algorithm math

我使用程序从数学运行Collat​​z猜想(http://en.wikipedia.org/wiki/Collatz_conjecture)。我实现了一个运行猜想算法的类(并返回输出)和一个创建固定线程池(使用我的处理器数量:8)的类,并接受Callable个调用猜想算法。

我为1之间的所有数字(输入类型必须是正整数)和400,000之间的数字创建了HashSet<Callable>。这似乎永远挂起,但较低的数字很好,这很奇怪。更奇怪的是,运行它似乎需要更长的时间来处理这些调用,而不是需要一个线程来处理相同数量的信息;它也大大增加了记忆力。

例如,在我的计算机上,程序花费不到一秒的时间来执行算法(只需一次迭代)400,000(最终值),所有较低的值花费较少的时间来计算(可能除了素数之外) ,花费更长的时间)我运行的是带有8GB内存的Windows 8.1和带有2.2Ghz的8个逻辑处理器。

代码:

private static void initThreads() throws InterruptedException {
    //Files.createDirectories(SEQUENCER_FOLDER_PATH);
    //Files.createFile(SEQUENCER_FILE_PATH);
    ExecutorService service = Executors.newFixedThreadPool(8, new ThreadFactory() {
        private BigInteger count = BigInteger.ZERO;

        @Override
        public Thread newThread(Runnable r) {
            count = count.add(BigInteger.ONE);
            return new Thread(r, "Collatz Sequencer Thread: " + count);
        }
    });
    int finalNumber = 400_000;
    final HashSet<Callable<Void>> tasks = new HashSet<>(finalNumber);
    for (long l = 1; l <= finalNumber; l++) {
        final BigInteger number = BigInteger.valueOf(l);
        tasks.add(() -> {
            CollatzSequencer sequencer = new CollatzSequencer(new BigInteger(number.toString()));
            synchronized (dataSet) {
                dataSet.put(number, sequencer.init());
            }
            return null;
        });
    }
    service.invokeAll(tasks);
    Thread dataThread = new Thread(() -> {
        while (true) {
            synchronized (dataSet) {
                if (dataSet.size() == finalNumber) {
                    System.err.println("Values: \n");
                    for (CollatzSequencer.FinalSequencerReport data : dataSet.values()) {
                        System.err.println("Entry: " + data.getInitialValue() + ", " + data.getIterations());
                    }
                    System.exit(0);
                }
            }
        }
    }, "Collatz Conjecture Data Set Thread");
    dataThread.start();
}

Collat​​z猜想算法:

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package com.collatzsequencer.core;

import java.math.BigInteger;

/**
 * A sequencer used for computing the collatz sequence.
 *
 * @author Sarah Szabo
 * @version 1.0
 */
public class CollatzSequencer {

private final BigInteger initialValue;

public CollatzSequencer(BigInteger currentValue) {
    if (currentValue == null) {
        throw new NullPointerException("Value passed can't be null");
    } else if (currentValue.compareTo(new BigInteger("1")) < 0) {
        throw new NumberFormatException("The value passed to the constructor must be a natural number.");
    }
    this.initialValue = currentValue;
}

public FinalSequencerReport init() {
    return new FinalSequencerReport(performOperation(new SequencerReport(this.initialValue)), this.initialValue);
}

private SequencerReport performOperation(SequencerReport report) {
    if (report.getResult().equals(new BigInteger("1"))) {
        return new SequencerReport(report.getResult(), report.getIterations(), report.getSequence().length() > 1
                ? report.getSequence().substring(0, report.getSequence().length() - 3) : "The sequence starts and ends at 1 <Nothing Done>");
    } else if (report.getResult().mod(new BigInteger("2")).equals(new BigInteger("0"))) {
        BigInteger value = report.getResult().divide(new BigInteger("2"));
        return performOperation(new SequencerReport(value, report.getIterations().add(new BigInteger("1")),
                report.getSequence() + " " + report.getResult() + "/2 -> " + value + " ->"));
    } else {
        BigInteger value = report.getResult().multiply(new BigInteger("3")).add(new BigInteger("1"));
        return performOperation(new SequencerReport(value, report.getIterations()
                .add(new BigInteger("1")), report.getSequence() + report.getResult() + " * 3 + 1 ->" + value + " ->"));
    }
}

public static final class FinalSequencerReport extends SequencerReport {

    private final BigInteger initialValue;
    private final String finalFormattedString;

    public FinalSequencerReport(SequencerReport finalReport, BigInteger initialValue) {
        super(finalReport.getResult(), finalReport.getIterations(), finalReport.getSequence());
        this.initialValue = initialValue;
        this.finalFormattedString = "Initial Value: "
                + getInitialValue() + "\nFinal Value: " + getResult() + "\nIterations:  "
                + getIterations() + "\nAlgebraic Sequence:\n" + getSequence();
    }

    public String getFinalFormattedString() {
        return finalFormattedString;
    }

    public BigInteger getInitialValue() {
        return initialValue;
    }
}

public static class SequencerReport {

    private final BigInteger result, iterations;
    private final String sequence;

    public SequencerReport(BigInteger result) {
        this(result, new BigInteger("0"), "");
    }

    public SequencerReport(BigInteger result, BigInteger iterations, String sequence) {
        this.result = result;
        this.iterations = iterations;
        this.sequence = sequence;
    }

    public BigInteger getResult() {
        return this.result;
    }

    public BigInteger getIterations() {
        return this.iterations;
    }

    public String getSequence() {
        return this.sequence;
    }

  }
}

1 个答案:

答案 0 :(得分:1)

正如你所说,你的代码有效;问题可能只是表现。我会尝试一些事情:

  1. 使用long代替BigIntegerBigInteger非常慢。
  2. 取代mod 2(或% 2),使用 & 1。二进制AND将有效地获得相同的结果并且速度更快。
  3. 你正在做的方式太多String操纵。覆盖sequencerReport.toString(),并在您打印数据时让所有toString电话完成。
  4. 不要做new ThreadFactory()。使用Guava's ThreadFactoryBuilder
    • 除非您真正知道自己在做什么,否则不应在代码中致电new Thread(),这意味着不要这样做。
  5. dataThread添加等待/通知机制,而不是忙循环。完成工作后致电dataSet.notify()dataSet.wait()正文内部dataThread