如何使用多线程在Java中编写PI计算程序?

时间:2016-12-12 04:05:00

标签: java multithreading pi approximation

我需要使用Java多线程创建一个可以计算常量PI近似值的程序。

我打算使用Gregory-Leibniz系列计算PI / 4的结果,然后乘以4得到PI近似值。

enter image description here

但我对该计划有些担忧:

  1. 如何分离计算过程以便我可以为程序实现多线程处理?因为公式是总和,我不知道如何将它们分成几部分,然后最后我将收集它们。
  2. 我正在考虑这样一个事实,即程序将执行公式无限,因此用户需要提供一些配置执行的方法,以确定何时应该停止并返回结果。是否有可能,我该怎么做?
  3. 到目前为止,这是迄今为止我能做的最多。

    public class PICalculate {
    
        public static void main(String[] args) {
            System.out.println(calculatePI(5000000) * 4);
        }
    
        static double calculatePI(int n) {
            double result = 0.0;
            if (n < 0) {
                return 0.0;
            }
            for (int i = 0; i <= n; i++) {
                result += Math.pow(-1, i) / ((2 * i) + 1);
            }
            return result;
        }
    }
    

1 个答案:

答案 0 :(得分:1)

最简单但不是最优的方法是在您拥有的线程之间分发序列元素。即,如果你有4个线程,线程1将使用n%4 == 0个元素,thread2与n%4 == 1个元素等等

public static void main(String ... args) throws InterruptedException {

    int threadCount = 4;
    int N = 100_000;
    PiThread[] threads = new PiThread[threadCount];
    for (int i = 0; i < threadCount; i++) {
        threads[i] = new PiThread(threadCount, i, N);
        threads[i].start();
    }
    for (int i = 0; i < threadCount; i++) {
        threads[i].join();
    }
    double pi = 0;
    for (int i = 0; i < threadCount; i++) {
        pi += threads[i].getSum();
    }
    System.out.print("PI/4 = " + pi);

}

static class PiThread extends Thread {

    private final int threadCount;
    private final int threadRemainder;
    private final int N;
    private double sum  = 0;

    public PiThread(int threadCount, int threadRemainder, int n) {
        this.threadCount = threadCount;
        this.threadRemainder = threadRemainder;
        N = n;
    }


    @Override
    public void run() {
        for (int i = 0; i <= N; i++) {
            if (i % threadCount == threadRemainder) {
                sum += Math.pow(-1, i) / (2 * i + 1);
            }
        }
    }

    public double getSum() {
        return sum;
    }
}

如果循环更短,PiThread更有效,但可能更难阅读:

public void run() {
    for (int i = threadRemainder; i <= N; i += threadCount) {
        sum += Math.pow(-1, i) / (2 * i + 1);
    }
}

如果您不想按顺序和时间限制自己的元素数量,您可以按照下面的方法。但是请注意,它仍然受限于Long.MAX_VALUE,你必须使用BigIntegers,BigDecimals或任何其他合理的方法来改进它

public static volatile boolean running = true;

public static void main(String ... args) throws InterruptedException {
    int threadCount = 4;
    long timeoutMs = 5_000;
    final AtomicLong counter = new AtomicLong(0);
    PiThread[] threads = new PiThread[threadCount];
    for (int i = 0; i < threadCount; i++) {
        threads[i] = new PiThread(counter);
        threads[i].start();
    }

    Thread.sleep(timeoutMs);
    running = false;

    for (int i = 0; i < threadCount; i++) {
        threads[i].join();
    }

    double sum = 0;
    for (int i = 0; i < threadCount; i++) {
        sum += threads[i].getSum();
    }
    System.out.print("counter = " + counter.get());
    System.out.print("PI = " + 4*sum);

}

static class PiThread extends Thread {

    private AtomicLong counter;
    private double sum  = 0;

    public PiThread(AtomicLong counter) {
        this.counter = counter;
    }


    @Override
    public void run() {
        long i;
        while (running && isValidCounter(i = counter.getAndAdd(1))) {
            sum += Math.pow(-1, i) / (2 * i + 1);
        }
    }

    private boolean isValidCounter(long value) {
        return value >= 0 && value < Long.MAX_VALUE;
    }

    public double getSum() {
        return sum;
    }
}