用Java计算PI。值有误

时间:2019-06-17 19:58:09

标签: java

我正在尝试使用Gregory-Leibniz算法来计算PI,但是我总是仅在使用多线程时才得到错误的值。单线程工作正常。

我认为问题是共享的k值导致计算混乱。

错误的PI值:

  

选择一个选项:4多少点? 100000多少个线程? 32   并发的Gregory-Leibniz估计PI值:2.7663972054374577。   在121.578657毫秒内执行。

请帮忙吗?

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

/**
 * The Class GregoryLeibniz.
 */
public class GregoryLeibniz {

    /** The in circle. */
    private double factor;

    /** The sum. */
    private double sum;

    /** The points. */
    private long points;

    /** The n processors. */
    private int nProcessors;

    private long k;


    /**
     * Instantiates a new gregory leibniz.
     *
     * @param points the points
     * @param nProcessors the n processors
     */
    public GregoryLeibniz(long points, int nProcessors) {
        super();
        this.points = points;
        this.nProcessors = nProcessors;
    }


    /**
     * The Class GregoryLeibnizImpl.
     */
    public class GregoryLeibnizImpl implements Runnable {   

        /* (non-Javadoc)
         * @see java.lang.Runnable#run()
         */
        @Override
        public void run() {
            if(k % 2 == 0) factor = 1.0;
            else factor = -1.0;

            sum += factor / (2*k +1);
        }
    }

    /**
     * Calculate PI.
     *
     * @return the double
     */
    public double calculatePI() {
        ExecutorService executor = Executors.newWorkStealingPool(nProcessors);

        for (k = 0; k < points; k++) {
            Runnable worker = new GregoryLeibnizImpl();
            executor.execute(worker);
        }

        executor.shutdown();

        while(!executor.isTerminated()) { }

        return 4.0 * sum;
    }
}

3 个答案:

答案 0 :(得分:2)

GregoryLeibnizImpl的每个实例都需要独立运行。或者您需要互斥锁。或两者兼有。

  1. GregoryLeibnizImpl需要将“ k”作为构造函数参数并将其存储为成员变量。

  2. 您需要在sum周围使用互斥/防护。否则,您需要在calculatePI函数末尾“总结”工作线程对象的所有结果。

此行:

while(!executor.isTerminated()) { }

将消耗整个内核并杀死代码性能。请改用awaitTermination方法。

更新

我想要一些练习,所以我重组了您的代码以寻求一个有价值的解决方案。也许我可以帮忙...

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

/**
 * The Class GregoryLeibniz.
 */
public  class GregoryLeibniz {

    /** The n processors. */
    private int nProcessors;
    private long points;
    private long itemsPerThread;
    private long itemsInFirstThread;

    /**
     * Instantiates a new gregory leibniz.
     *
     * @param points the points
     * @param nProcessors the n processors
     */
    public GregoryLeibniz(long points, int nProcessors) {
        this.points = points;
        this.nProcessors = nProcessors;
        this.itemsPerThread = this.points / this.nProcessors;
        this.itemsInFirstThread += this.itemsPerThread + this.points - this.itemsPerThread * this.nProcessors;
    }


    /**
     * The Class GregoryLeibnizImpl.
     */
    public class GregoryLeibnizImpl implements Runnable {

        /* (non-Javadoc)
         * @see java.lang.Runnable#run()
         */

        long start;
        long end;
        public double result;

        public GregoryLeibnizImpl(long start, long end)
        {
            this.start = start;
            this.end = end;
        }

        @Override
        public void run() {
            int factor = ((start % 2)!=0) ? -1 : 1;
            for (long i = start; i <= end; i++) {

                result += factor / (double)(i*2+1);
                factor *= -1;
            }
        }
    }

    /**
     * Calculate PI.
     *
     * @return the double
     */
    public double calculatePI() {
        ExecutorService executor = Executors.newWorkStealingPool(nProcessors);

        long start = 1;
        long end = itemsInFirstThread;

        GregoryLeibnizImpl [] workers = new GregoryLeibnizImpl[this.nProcessors];

        for (int t = 0; t < this.nProcessors; t++) {
            GregoryLeibnizImpl worker = new GregoryLeibnizImpl(start, end);
            workers[t] = worker;
            executor.execute(worker);
            start += this.itemsPerThread;
            end += this.itemsPerThread;
        }

        executor.shutdown();

        while (executor.isTerminated() == false) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
            }
        }

        double result = 0;
        for (int t = 0; t < this.nProcessors; t++) {
            result += workers[t].result;
        }

        result += 1;
        result *= 4;

        return result;

    }

    public static void main(String [] args) {

        var gl = new GregoryLeibniz(1000000, 4);
        double d = gl.calculatePI();
        System.out.println(d);
    }

}

答案 1 :(得分:0)

尝试同步运行方法。如果让k不使用互斥锁,则一个线程将检查k的模数,设置系数,然后它将进入睡眠状态,而下一个线程将检查模数的设置(相同)因数,求和+ =填充并进入睡眠状态。在那之后,第一个线程也将用旧因子对sum ==进行求和。

答案 2 :(得分:0)

您需要给您的工作人员k的副本。除此之外,让您的工人只迈出第一步,您并不能真正赢得很多。创建的工作人员不应超过可用的CPU(核)数量,并为每个工作人员分配一部分用于计算。

还请注意,这仍然给您带来错误的sum,因为如果两个工作人员同时对其进行读写,那么其中一个结果将会丢失。

让每个工作人员维护一个私有sum,然后在完成所有工作后,将其私有总和添加到主线程中。

类似这样的东西:

public class GregoryLeibnizImpl implements Runnable {
    private int start;
    private int end;
    private int sum;

    public GregoryLeibnizImpl(int start, int end) {
        this.start = start;
        this.end = end;
    }

    @Override
    public void run() {
        // loop from start to end using your sum algorithm
    }
}

然后将循环更改为以下内容:

    for (int k = 0; k < points; k += points / numthreads) {
        Runnable worker = new GregoryLeibnizImpl(k, k + points / numthreads);
        executor.execute(worker);
    }

您需要将这些工作实例保存在某个地方,例如一个列表,以便一旦完成就可以收集其结果。